summaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/common/saa7146_fops.c126
-rw-r--r--drivers/media/common/saa7146_hlp.c23
-rw-r--r--drivers/media/common/saa7146_vbi.c54
-rw-r--r--drivers/media/common/saa7146_video.c367
-rw-r--r--drivers/media/common/tuners/Kconfig27
-rw-r--r--drivers/media/common/tuners/Makefile4
-rw-r--r--drivers/media/common/tuners/fc0011.c524
-rw-r--r--drivers/media/common/tuners/fc0011.h41
-rw-r--r--drivers/media/common/tuners/fc0012-priv.h43
-rw-r--r--drivers/media/common/tuners/fc0012.c467
-rw-r--r--drivers/media/common/tuners/fc0012.h44
-rw-r--r--drivers/media/common/tuners/fc0013-priv.h44
-rw-r--r--drivers/media/common/tuners/fc0013.c634
-rw-r--r--drivers/media/common/tuners/fc0013.h57
-rw-r--r--drivers/media/common/tuners/fc001x-common.h39
-rw-r--r--drivers/media/common/tuners/tua9001.c215
-rw-r--r--drivers/media/common/tuners/tua9001.h46
-rw-r--r--drivers/media/common/tuners/tua9001_priv.h34
-rw-r--r--drivers/media/common/tuners/xc5000.c7
-rw-r--r--drivers/media/common/tuners/xc5000.h2
-rw-r--r--drivers/media/dvb/bt8xx/dst_ca.c2
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-core.c3
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.c10
-rw-r--r--drivers/media/dvb/dvb-core/dvb_demux.h2
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c80
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h18
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig13
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c495
-rw-r--r--drivers/media/dvb/dvb-usb/af9035.c1242
-rw-r--r--drivers/media/dvb/dvb-usb/af9035.h113
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c24
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_devices.c7
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h12
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-urb.c12
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb.h3
-rw-r--r--drivers/media/dvb/dvb-usb/dw2102.c76
-rw-r--r--drivers/media/dvb/dvb-usb/it913x.c4
-rw-r--r--drivers/media/dvb/dvb-usb/lmedm04.c5
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf-tuner.c1
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.c850
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.c28
-rw-r--r--drivers/media/dvb/frontends/Kconfig35
-rw-r--r--drivers/media/dvb/frontends/Makefile7
-rw-r--r--drivers/media/dvb/frontends/af9013.c13
-rw-r--r--drivers/media/dvb/frontends/af9033.c980
-rw-r--r--drivers/media/dvb/frontends/af9033.h75
-rw-r--r--drivers/media/dvb/frontends/af9033_priv.h470
-rw-r--r--drivers/media/dvb/frontends/au8522_common.c259
-rw-r--r--drivers/media/dvb/frontends/au8522_dig.c215
-rw-r--r--drivers/media/dvb/frontends/au8522_priv.h2
-rw-r--r--drivers/media/dvb/frontends/cx24110.c7
-rw-r--r--drivers/media/dvb/frontends/cxd2820r_core.c4
-rw-r--r--drivers/media/dvb/frontends/dib7000p.c5
-rw-r--r--drivers/media/dvb/frontends/dib9000.c131
-rw-r--r--drivers/media/dvb/frontends/drxd.h14
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.c18
-rw-r--r--drivers/media/dvb/frontends/drxk_map.h2
-rw-r--r--drivers/media/dvb/frontends/ds3000.c5
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.c26
-rw-r--r--drivers/media/dvb/frontends/lg2160.c1468
-rw-r--r--drivers/media/dvb/frontends/lg2160.h84
-rw-r--r--drivers/media/dvb/frontends/lgs8gxx.c3
-rw-r--r--drivers/media/dvb/frontends/m88rs2000.c29
-rw-r--r--drivers/media/dvb/frontends/rtl2830.c201
-rw-r--r--drivers/media/dvb/frontends/rtl2830_priv.h1
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c8
-rw-r--r--drivers/media/dvb/frontends/stb6100.c3
-rw-r--r--drivers/media/dvb/frontends/stv0297.c2
-rw-r--r--drivers/media/dvb/frontends/stv0900_sw.c2
-rw-r--r--drivers/media/dvb/frontends/stv090x.c2
-rw-r--r--drivers/media/dvb/frontends/zl10353.c5
-rw-r--r--drivers/media/dvb/mantis/hopper_cards.c3
-rw-r--r--drivers/media/dvb/mantis/mantis_cards.c3
-rw-r--r--drivers/media/dvb/mantis/mantis_dma.c4
-rw-r--r--drivers/media/dvb/mantis/mantis_evm.c3
-rw-r--r--drivers/media/dvb/ngene/ngene-core.c4
-rw-r--r--drivers/media/dvb/pluto2/pluto2.c8
-rw-r--r--drivers/media/dvb/siano/smssdio.c4
-rw-r--r--drivers/media/dvb/siano/smsusb.c2
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c72
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c6
-rw-r--r--drivers/media/media-entity.c57
-rw-r--r--drivers/media/radio/Kconfig4
-rw-r--r--drivers/media/radio/dsbr100.c528
-rw-r--r--drivers/media/radio/radio-gemtek.c25
-rw-r--r--drivers/media/radio/radio-isa.c173
-rw-r--r--drivers/media/radio/radio-isa.h9
-rw-r--r--drivers/media/radio/radio-keene.c36
-rw-r--r--drivers/media/radio/radio-mr800.c524
-rw-r--r--drivers/media/radio/radio-rtrack2.c1
-rw-r--r--drivers/media/radio/radio-sf16fmi.c14
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c144
-rw-r--r--drivers/media/radio/radio-timb.c2
-rw-r--r--drivers/media/radio/saa7706h.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-common.c305
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c65
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c265
-rw-r--r--drivers/media/radio/si470x/radio-si470x.h14
-rw-r--r--drivers/media/radio/tef6862.c2
-rw-r--r--drivers/media/radio/wl128x/fmdrv_v4l2.c4
-rw-r--r--drivers/media/rc/Kconfig1
-rw-r--r--drivers/media/rc/ati_remote.c146
-rw-r--r--drivers/media/rc/fintek-cir.c13
-rw-r--r--drivers/media/rc/imon.c2
-rw-r--r--drivers/media/rc/ir-raw.c8
-rw-r--r--drivers/media/rc/ir-sanyo-decoder.c4
-rw-r--r--drivers/media/rc/ite-cir.c14
-rw-r--r--drivers/media/rc/keymaps/Makefile3
-rw-r--r--drivers/media/rc/keymaps/rc-asus-ps3-100.c91
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v2.c2
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10-digitainer.c123
-rw-r--r--drivers/media/rc/keymaps/rc-medion-x10-or2x.c108
-rw-r--r--drivers/media/rc/mceusb.c5
-rw-r--r--drivers/media/rc/nuvoton-cir.c26
-rw-r--r--drivers/media/rc/rc-loopback.c1
-rw-r--r--drivers/media/rc/redrat3.c2
-rw-r--r--drivers/media/video/Kconfig48
-rw-r--r--drivers/media/video/Makefile5
-rw-r--r--drivers/media/video/adp1653.c9
-rw-r--r--drivers/media/video/adv7180.c417
-rw-r--r--drivers/media/video/adv7343.c4
-rw-r--r--drivers/media/video/aptina-pll.c5
-rw-r--r--drivers/media/video/arv.c7
-rw-r--r--drivers/media/video/as3645a.c10
-rw-r--r--drivers/media/video/atmel-isi.c18
-rw-r--r--drivers/media/video/au0828/Kconfig3
-rw-r--r--drivers/media/video/au0828/au0828-cards.c2
-rw-r--r--drivers/media/video/au0828/au0828-dvb.c27
-rw-r--r--drivers/media/video/au0828/au0828-video.c25
-rw-r--r--drivers/media/video/blackfin/bfin_capture.c4
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c4
-rw-r--r--drivers/media/video/bw-qcam.c132
-rw-r--r--drivers/media/video/c-qcam.c140
-rw-r--r--drivers/media/video/cpia2/cpia2.h34
-rw-r--r--drivers/media/video/cpia2/cpia2_core.c142
-rw-r--r--drivers/media/video/cpia2/cpia2_usb.c78
-rw-r--r--drivers/media/video/cpia2/cpia2_v4l.c850
-rw-r--r--drivers/media/video/cpia2/cpia2dev.h50
-rw-r--r--drivers/media/video/cx18/cx18-alsa-main.c1
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.c10
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c2
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c6
-rw-r--r--drivers/media/video/cx18/cx18-streams.c3
-rw-r--r--drivers/media/video/cx231xx/cx231xx-417.c18
-rw-r--r--drivers/media/video/cx231xx/cx231xx-audio.c18
-rw-r--r--drivers/media/video/cx231xx/cx231xx-avcore.c148
-rw-r--r--drivers/media/video/cx231xx/cx231xx-core.c76
-rw-r--r--drivers/media/video/cx231xx/cx231xx-vbi.c6
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c20
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c9
-rw-r--r--drivers/media/video/cx23885/cx23885-core.c7
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c7
-rw-r--r--drivers/media/video/cx23885/cx23885.h1
-rw-r--r--drivers/media/video/cx23885/cx23888-ir.c4
-rw-r--r--drivers/media/video/cx25821/cx25821-alsa.c2
-rw-r--r--drivers/media/video/cx25821/cx25821-audio-upstream.c3
-rw-r--r--drivers/media/video/cx25821/cx25821-core.c14
-rw-r--r--drivers/media/video/cx25821/cx25821-i2c.c3
-rw-r--r--drivers/media/video/cx25821/cx25821-medusa-video.c13
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream-ch2.c3
-rw-r--r--drivers/media/video/cx25821/cx25821-video-upstream.c3
-rw-r--r--drivers/media/video/cx25821/cx25821-video.c25
-rw-r--r--drivers/media/video/cx25821/cx25821-video.h2
-rw-r--r--drivers/media/video/cx25840/cx25840-ir.c6
-rw-r--r--drivers/media/video/davinci/Kconfig1
-rw-r--r--drivers/media/video/davinci/vpbe_display.c4
-rw-r--r--drivers/media/video/davinci/vpfe_capture.c2
-rw-r--r--drivers/media/video/davinci/vpif_capture.c4
-rw-r--r--drivers/media/video/davinci/vpif_display.c4
-rw-r--r--drivers/media/video/em28xx/Kconfig4
-rw-r--r--drivers/media/video/em28xx/Makefile5
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c11
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c81
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c30
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c11
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c3
-rw-r--r--drivers/media/video/em28xx/em28xx-input.c250
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c13
-rw-r--r--drivers/media/video/em28xx/em28xx.h60
-rw-r--r--drivers/media/video/et61x251/Kconfig18
-rw-r--r--drivers/media/video/et61x251/Makefile4
-rw-r--r--drivers/media/video/et61x251/et61x251.h213
-rw-r--r--drivers/media/video/et61x251/et61x251_core.c2683
-rw-r--r--drivers/media/video/et61x251/et61x251_sensor.h108
-rw-r--r--drivers/media/video/et61x251/et61x251_tas5130d1b.c143
-rw-r--r--drivers/media/video/fsl-viu.c4
-rw-r--r--drivers/media/video/gspca/Makefile2
-rw-r--r--drivers/media/video/gspca/autogain_functions.c178
-rw-r--r--drivers/media/video/gspca/autogain_functions.h6
-rw-r--r--drivers/media/video/gspca/conex.c4
-rw-r--r--drivers/media/video/gspca/finepix.c18
-rw-r--r--drivers/media/video/gspca/gl860/gl860.c3
-rw-r--r--drivers/media/video/gspca/gspca.c545
-rw-r--r--drivers/media/video/gspca/gspca.h26
-rw-r--r--drivers/media/video/gspca/jl2005bcd.c10
-rw-r--r--drivers/media/video/gspca/mars.c292
-rw-r--r--drivers/media/video/gspca/nw80x.c2
-rw-r--r--drivers/media/video/gspca/ov519.c10
-rw-r--r--drivers/media/video/gspca/ov534.c146
-rw-r--r--drivers/media/video/gspca/ov534_9.c2
-rw-r--r--drivers/media/video/gspca/pac207.c336
-rw-r--r--drivers/media/video/gspca/pac7302.c185
-rw-r--r--drivers/media/video/gspca/pac7311.c505
-rw-r--r--drivers/media/video/gspca/sn9c20x.c594
-rw-r--r--drivers/media/video/gspca/sonixb.c2
-rw-r--r--drivers/media/video/gspca/sonixj.c5
-rw-r--r--drivers/media/video/gspca/sq905.c12
-rw-r--r--drivers/media/video/gspca/sq905c.c10
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.c21
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx.h3
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c143
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h7
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c359
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h12
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_sensor.h4
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_st6422.c236
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_st6422.h4
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c198
-rw-r--r--drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h8
-rw-r--r--drivers/media/video/gspca/topro.c6
-rw-r--r--drivers/media/video/gspca/vicam.c13
-rw-r--r--drivers/media/video/gspca/zc3xx.c620
-rw-r--r--drivers/media/video/hdpvr/hdpvr-control.c2
-rw-r--r--drivers/media/video/hdpvr/hdpvr-core.c4
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c2
-rw-r--r--drivers/media/video/hexium_gemini.c129
-rw-r--r--drivers/media/video/hexium_orion.c24
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c4
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c6
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c8
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c4
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c2
-rw-r--r--drivers/media/video/m5mols/m5mols.h81
-rw-r--r--drivers/media/video/m5mols/m5mols_capture.c11
-rw-r--r--drivers/media/video/m5mols/m5mols_controls.c479
-rw-r--r--drivers/media/video/m5mols/m5mols_core.c93
-rw-r--r--drivers/media/video/m5mols/m5mols_reg.h1
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c1
-rw-r--r--drivers/media/video/mem2mem_testdev.c6
-rw-r--r--drivers/media/video/meye.c2
-rw-r--r--drivers/media/video/msp3400-driver.c15
-rw-r--r--drivers/media/video/mt9m032.c4
-rw-r--r--drivers/media/video/mt9p031.c161
-rw-r--r--drivers/media/video/mt9t112.c1
-rw-r--r--drivers/media/video/mt9v032.c2
-rw-r--r--drivers/media/video/mx1_camera.c14
-rw-r--r--drivers/media/video/mx2_camera.c78
-rw-r--r--drivers/media/video/mx2_emmaprp.c8
-rw-r--r--drivers/media/video/mx3_camera.c41
-rw-r--r--drivers/media/video/mxb.c351
-rw-r--r--drivers/media/video/mxb.h42
-rw-r--r--drivers/media/video/omap1_camera.c22
-rw-r--r--drivers/media/video/omap24xxcam-dma.c20
-rw-r--r--drivers/media/video/omap24xxcam.c3
-rw-r--r--drivers/media/video/omap24xxcam.h14
-rw-r--r--drivers/media/video/omap3isp/isp.c59
-rw-r--r--drivers/media/video/omap3isp/isp.h8
-rw-r--r--drivers/media/video/omap3isp/ispccdc.c256
-rw-r--r--drivers/media/video/omap3isp/ispccdc.h12
-rw-r--r--drivers/media/video/omap3isp/ispccp2.c24
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.c21
-rw-r--r--drivers/media/video/omap3isp/ispcsi2.h1
-rw-r--r--drivers/media/video/omap3isp/ispcsiphy.c4
-rw-r--r--drivers/media/video/omap3isp/ispcsiphy.h15
-rw-r--r--drivers/media/video/omap3isp/isppreview.c634
-rw-r--r--drivers/media/video/omap3isp/isppreview.h76
-rw-r--r--drivers/media/video/omap3isp/ispqueue.h2
-rw-r--r--drivers/media/video/omap3isp/ispresizer.c139
-rw-r--r--drivers/media/video/omap3isp/ispstat.c2
-rw-r--r--drivers/media/video/omap3isp/ispvideo.c303
-rw-r--r--drivers/media/video/omap3isp/ispvideo.h5
-rw-r--r--drivers/media/video/ov5642.c2
-rw-r--r--drivers/media/video/pms.c239
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h6
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c193
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h9
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c1341
-rw-r--r--drivers/media/video/pwc/pwc-if.c191
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c145
-rw-r--r--drivers/media/video/pwc/pwc.h21
-rw-r--r--drivers/media/video/pxa_camera.c15
-rw-r--r--drivers/media/video/s2255drv.c11
-rw-r--r--drivers/media/video/s5p-fimc/Kconfig48
-rw-r--r--drivers/media/video/s5p-fimc/Makefile6
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c506
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c1159
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h272
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite-reg.c300
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite-reg.h150
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite.c1576
-rw-r--r--drivers/media/video/s5p-fimc/fimc-lite.h213
-rw-r--r--drivers/media/video/s5p-fimc/fimc-m2m.c824
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c476
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.h18
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.c616
-rw-r--r--drivers/media/video/s5p-fimc/fimc-reg.h326
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.c21
-rw-r--r--drivers/media/video/s5p-fimc/regs-fimc.h301
-rw-r--r--drivers/media/video/s5p-g2d/g2d.c69
-rw-r--r--drivers/media/video/s5p-g2d/g2d.h1
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.c68
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.h2
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc.c81
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_common.h2
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c16
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_enc.c6
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_opr.c28
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c480
-rw-r--r--drivers/media/video/s5p-tv/hdmiphy_drv.c225
-rw-r--r--drivers/media/video/s5p-tv/mixer.h3
-rw-r--r--drivers/media/video/s5p-tv/mixer_drv.c2
-rw-r--r--drivers/media/video/s5p-tv/mixer_reg.c15
-rw-r--r--drivers/media/video/s5p-tv/mixer_video.c10
-rw-r--r--drivers/media/video/s5p-tv/regs-hdmi.h1
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c45
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c39
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c7
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c2
-rw-r--r--drivers/media/video/saa7134/saa7134.h1
-rw-r--r--drivers/media/video/saa7164/saa7164-vbi.c4
-rw-r--r--drivers/media/video/saa7164/saa7164.h5
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c92
-rw-r--r--drivers/media/video/sh_vou.c4
-rw-r--r--drivers/media/video/smiapp-pll.c418
-rw-r--r--drivers/media/video/smiapp-pll.h103
-rw-r--r--drivers/media/video/smiapp/Kconfig6
-rw-r--r--drivers/media/video/smiapp/Makefile5
-rw-r--r--drivers/media/video/smiapp/smiapp-core.c2894
-rw-r--r--drivers/media/video/smiapp/smiapp-limits.c132
-rw-r--r--drivers/media/video/smiapp/smiapp-limits.h128
-rw-r--r--drivers/media/video/smiapp/smiapp-quirk.c306
-rw-r--r--drivers/media/video/smiapp/smiapp-quirk.h83
-rw-r--r--drivers/media/video/smiapp/smiapp-reg-defs.h503
-rw-r--r--drivers/media/video/smiapp/smiapp-reg.h122
-rw-r--r--drivers/media/video/smiapp/smiapp-regs.c273
-rw-r--r--drivers/media/video/smiapp/smiapp-regs.h49
-rw-r--r--drivers/media/video/smiapp/smiapp.h252
-rw-r--r--drivers/media/video/sn9c102/sn9c102_core.c4
-rw-r--r--drivers/media/video/soc_camera.c55
-rw-r--r--drivers/media/video/soc_mediabus.c54
-rw-r--r--drivers/media/video/sta2x11_vip.c1550
-rw-r--r--drivers/media/video/sta2x11_vip.h40
-rw-r--r--drivers/media/video/stk-webcam.c8
-rw-r--r--drivers/media/video/tda9840.c75
-rw-r--r--drivers/media/video/tlg2300/pd-video.c1
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c3
-rw-r--r--drivers/media/video/tm6000/tm6000-stds.c2
-rw-r--r--drivers/media/video/tm6000/tm6000-video.c14
-rw-r--r--drivers/media/video/tm6000/tm6000.h2
-rw-r--r--drivers/media/video/tuner-core.c15
-rw-r--r--drivers/media/video/tvp5150.c11
-rw-r--r--drivers/media/video/tvp7002.c105
-rw-r--r--drivers/media/video/usbvision/usbvision-core.c12
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c4
-rw-r--r--drivers/media/video/uvc/uvc_ctrl.c330
-rw-r--r--drivers/media/video/uvc/uvc_queue.c43
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c50
-rw-r--r--drivers/media/video/uvc/uvcvideo.h26
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c15
-rw-r--r--drivers/media/video/v4l2-ctrls.c302
-rw-r--r--drivers/media/video/v4l2-dev.c218
-rw-r--r--drivers/media/video/v4l2-event.c71
-rw-r--r--drivers/media/video/v4l2-ioctl.c662
-rw-r--r--drivers/media/video/v4l2-subdev.c143
-rw-r--r--drivers/media/video/via-camera.c15
-rw-r--r--drivers/media/video/videobuf-core.c3
-rw-r--r--drivers/media/video/videobuf-dma-contig.c199
-rw-r--r--drivers/media/video/videobuf-dvb.c3
-rw-r--r--drivers/media/video/videobuf2-core.c50
-rw-r--r--drivers/media/video/vivi.c223
-rw-r--r--drivers/media/video/w9966.c94
-rw-r--r--drivers/media/video/zoran/zoran_device.c2
-rw-r--r--drivers/media/video/zoran/zoran_driver.c20
-rw-r--r--drivers/media/video/zr364xx.c2
375 files changed, 30687 insertions, 15718 deletions
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 71f8e018e56..7d42c11c868 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -198,7 +198,6 @@ static int fops_open(struct file *file)
struct saa7146_dev *dev = video_drvdata(file);
struct saa7146_fh *fh = NULL;
int result = 0;
-
enum v4l2_buf_type type;
DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
@@ -227,11 +226,12 @@ static int fops_open(struct file *file)
goto out;
}
- file->private_data = fh;
+ v4l2_fh_init(&fh->fh, vdev);
+
+ file->private_data = &fh->fh;
fh->dev = dev;
- fh->type = type;
- if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
DEB_S("initializing vbi...\n");
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
result = saa7146_vbi_uops.open(dev,file);
@@ -252,6 +252,7 @@ static int fops_open(struct file *file)
}
result = 0;
+ v4l2_fh_add(&fh->fh);
out:
if (fh && result != 0) {
kfree(fh);
@@ -263,6 +264,7 @@ out:
static int fops_release(struct file *file)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
@@ -271,7 +273,7 @@ static int fops_release(struct file *file)
if (mutex_lock_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
- if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
saa7146_vbi_uops.release(dev,file);
if (dev->ext_vv_data->vbi_fops.release)
@@ -280,6 +282,8 @@ static int fops_release(struct file *file)
saa7146_video_uops.release(dev,file);
}
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
module_put(dev->ext->module);
file->private_data = NULL;
kfree(fh);
@@ -291,19 +295,22 @@ static int fops_release(struct file *file)
static int fops_mmap(struct file *file, struct vm_area_struct * vma)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
struct videobuf_queue *q;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER: {
DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
file, vma);
q = &fh->video_q;
break;
}
- case V4L2_BUF_TYPE_VBI_CAPTURE: {
+ case VFL_TYPE_VBI: {
DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
file, vma);
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return -ENODEV;
q = &fh->vbi_q;
break;
}
@@ -317,15 +324,19 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma)
static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
struct videobuf_buffer *buf = NULL;
struct videobuf_queue *q;
+ unsigned int res = v4l2_ctrl_poll(file, wait);
DEB_EE("file:%p, poll:%p\n", file, wait);
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
+ if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
+ return res | POLLOUT | POLLWRNORM;
if( 0 == fh->vbi_q.streaming )
- return videobuf_poll_stream(file, &fh->vbi_q, wait);
+ return res | videobuf_poll_stream(file, &fh->vbi_q, wait);
q = &fh->vbi_q;
} else {
DEB_D("using video queue\n");
@@ -337,31 +348,32 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
if (!buf) {
DEB_D("buf == NULL!\n");
- return POLLERR;
+ return res | POLLERR;
}
poll_wait(file, &buf->done, wait);
if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
DEB_D("poll succeeded!\n");
- return POLLIN|POLLRDNORM;
+ return res | POLLIN | POLLRDNORM;
}
DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
- return 0;
+ return res;
}
static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
/*
DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun",
file, data, (unsigned long)count);
*/
return saa7146_video_uops.read(file,data,count,ppos);
- case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case VFL_TYPE_VBI:
/*
DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
file, data, (unsigned long)count);
@@ -377,12 +389,13 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof
static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
- switch (fh->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ switch (vdev->vfl_type) {
+ case VFL_TYPE_GRABBER:
return -EINVAL;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
+ case VFL_TYPE_VBI:
if (fh->dev->ext_vv_data->vbi_fops.write)
return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
else
@@ -429,8 +442,15 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status)
}
}
+static const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
+ .s_ctrl = saa7146_s_ctrl,
+};
+
int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
+ struct v4l2_pix_format *fmt;
+ struct v4l2_vbi_format *vbi;
struct saa7146_vv *vv;
int err;
@@ -438,12 +458,32 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
if (err)
return err;
+ v4l2_ctrl_handler_init(hdl, 6);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 127, 1, 64);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (hdl->error) {
+ err = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+ dev->v4l2_dev.ctrl_handler = hdl;
+
vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
if (vv == NULL) {
ERR("out of memory. aborting.\n");
+ v4l2_ctrl_handler_free(hdl);
return -ENOMEM;
}
- ext_vv->ops = saa7146_video_ioctl_ops;
+ ext_vv->vid_ops = saa7146_video_ioctl_ops;
+ ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
ext_vv->core_ops = &saa7146_video_ioctl_ops;
DEB_EE("dev:%p\n", dev);
@@ -463,6 +503,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
if( NULL == vv->d_clipping.cpu_addr ) {
ERR("out of memory. aborting.\n");
kfree(vv);
+ v4l2_ctrl_handler_free(hdl);
return -1;
}
memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
@@ -471,6 +512,39 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
saa7146_vbi_uops.init(dev,vv);
+ fmt = &vv->ov_fb.fmt;
+ fmt->width = vv->standard->h_max_out;
+ fmt->height = vv->standard->v_max_out;
+ fmt->pixelformat = V4L2_PIX_FMT_RGB565;
+ fmt->bytesperline = 2 * fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt = &vv->video_fmt;
+ fmt->width = 384;
+ fmt->height = 288;
+ fmt->pixelformat = V4L2_PIX_FMT_BGR24;
+ fmt->field = V4L2_FIELD_ANY;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt->bytesperline = 3 * fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+
+ vbi = &vv->vbi_fmt;
+ vbi->sampling_rate = 27000000;
+ vbi->offset = 248; /* todo */
+ vbi->samples_per_line = 720 * 2;
+ vbi->sample_format = V4L2_PIX_FMT_GREY;
+
+ /* fixme: this only works for PAL */
+ vbi->start[0] = 5;
+ vbi->count[0] = 16;
+ vbi->start[1] = 312;
+ vbi->count[1] = 16;
+
+ init_timer(&vv->vbi_read_timeout);
+
+ vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
dev->vv_data = vv;
dev->vv_callback = &vv_callback;
@@ -486,6 +560,7 @@ int saa7146_vv_release(struct saa7146_dev* dev)
v4l2_device_unregister(&dev->v4l2_dev);
pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
kfree(vv);
dev->vv_data = NULL;
dev->vv_callback = NULL;
@@ -509,10 +584,19 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
return -ENOMEM;
vfd->fops = &video_fops;
- vfd->ioctl_ops = &dev->ext_vv_data->ops;
+ if (type == VFL_TYPE_GRABBER)
+ vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
+ else
+ vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
vfd->release = video_device_release;
+ /* Locking in file operations other than ioctl should be done by
+ the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
vfd->lock = &dev->v4l2_lock;
+ vfd->v4l2_dev = &dev->v4l2_dev;
vfd->tvnorms = 0;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
for (i = 0; i < dev->ext_vv_data->num_stds; i++)
vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
strlcpy(vfd->name, name, sizeof(vfd->name));
diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c
index bc1f545c95c..be746d1aee9 100644
--- a/drivers/media/common/saa7146_hlp.c
+++ b/drivers/media/common/saa7146_hlp.c
@@ -343,9 +343,9 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa
struct saa7146_vv *vv = dev->vv_data;
__le32 *clipping = vv->d_clipping.cpu_addr;
- int width = fh->ov.win.w.width;
- int height = fh->ov.win.w.height;
- int clipcount = fh->ov.nclips;
+ int width = vv->ov.win.w.width;
+ int height = vv->ov.win.w.height;
+ int clipcount = vv->ov.nclips;
u32 line_list[32];
u32 pixel_list[32];
@@ -365,10 +365,10 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa
for(i = 0; i < clipcount; i++) {
int l = 0, r = 0, t = 0, b = 0;
- x[i] = fh->ov.clips[i].c.left;
- y[i] = fh->ov.clips[i].c.top;
- w[i] = fh->ov.clips[i].c.width;
- h[i] = fh->ov.clips[i].c.height;
+ x[i] = vv->ov.clips[i].c.left;
+ y[i] = vv->ov.clips[i].c.top;
+ w[i] = vv->ov.clips[i].c.width;
+ h[i] = vv->ov.clips[i].c.height;
if( w[i] < 0) {
x[i] += w[i]; w[i] = -w[i];
@@ -485,13 +485,14 @@ static void saa7146_disable_clipping(struct saa7146_dev *dev)
static void saa7146_set_clipping_rect(struct saa7146_fh *fh)
{
struct saa7146_dev *dev = fh->dev;
- enum v4l2_field field = fh->ov.win.field;
+ struct saa7146_vv *vv = dev->vv_data;
+ enum v4l2_field field = vv->ov.win.field;
struct saa7146_video_dma vdma2;
u32 clip_format;
u32 arbtr_ctrl;
/* check clipcount, disable clipping if clipcount == 0*/
- if( fh->ov.nclips == 0 ) {
+ if (vv->ov.nclips == 0) {
saa7146_disable_clipping(dev);
return;
}
@@ -651,8 +652,8 @@ int saa7146_enable_overlay(struct saa7146_fh *fh)
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
- saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field);
- saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field, vv->ov_fmt->pixelformat);
+ saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field);
+ saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat);
saa7146_set_output_format(dev, vv->ov_fmt->trans);
saa7146_set_clipping_rect(fh);
diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c
index b2e71834373..1e71e374bbf 100644
--- a/drivers/media/common/saa7146_vbi.c
+++ b/drivers/media/common/saa7146_vbi.c
@@ -211,7 +211,7 @@ static int buffer_activate(struct saa7146_dev *dev,
DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
saa7146_set_vbi_capture(dev,buf,next);
- mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
@@ -294,7 +294,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_VBI("vb:%p\n", vb);
- saa7146_buffer_queue(dev,&vv->vbi_q,buf);
+ saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf);
}
static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
@@ -335,16 +335,15 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file)
/* shut down dma 3 transfers */
saa7146_write(dev, MC1, MASK_20);
- if (vv->vbi_q.curr) {
- saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
- }
+ if (vv->vbi_dmaq.curr)
+ saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
videobuf_queue_cancel(&fh->vbi_q);
vv->vbi_streaming = NULL;
- del_timer(&vv->vbi_q.timeout);
- del_timer(&fh->vbi_read_timeout);
+ del_timer(&vv->vbi_dmaq.timeout);
+ del_timer(&vv->vbi_read_timeout);
spin_unlock_irqrestore(&dev->slock, flags);
}
@@ -364,12 +363,12 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
{
DEB_VBI("dev:%p\n", dev);
- INIT_LIST_HEAD(&vv->vbi_q.queue);
+ INIT_LIST_HEAD(&vv->vbi_dmaq.queue);
- init_timer(&vv->vbi_q.timeout);
- vv->vbi_q.timeout.function = saa7146_buffer_timeout;
- vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q);
- vv->vbi_q.dev = dev;
+ init_timer(&vv->vbi_dmaq.timeout);
+ vv->vbi_dmaq.timeout.function = saa7146_buffer_timeout;
+ vv->vbi_dmaq.timeout.data = (unsigned long)(&vv->vbi_dmaq);
+ vv->vbi_dmaq.dev = dev;
init_waitqueue_head(&vv->vbi_wq);
}
@@ -377,6 +376,7 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
static int vbi_open(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = fh->dev->vv_data;
u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
int ret = 0;
@@ -395,19 +395,6 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file)
saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
saa7146_write(dev, MC2, (MASK_04|MASK_20));
- memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt));
-
- fh->vbi_fmt.sampling_rate = 27000000;
- fh->vbi_fmt.offset = 248; /* todo */
- fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture;
- fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY;
-
- /* fixme: this only works for PAL */
- fh->vbi_fmt.start[0] = 5;
- fh->vbi_fmt.count[0] = 16;
- fh->vbi_fmt.start[1] = 312;
- fh->vbi_fmt.count[1] = 16;
-
videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
@@ -415,9 +402,8 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file)
sizeof(struct saa7146_buf),
file, &dev->v4l2_lock);
- init_timer(&fh->vbi_read_timeout);
- fh->vbi_read_timeout.function = vbi_read_timeout;
- fh->vbi_read_timeout.data = (unsigned long)file;
+ vv->vbi_read_timeout.function = vbi_read_timeout;
+ vv->vbi_read_timeout.data = (unsigned long)file;
/* initialize the brs */
if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
@@ -453,16 +439,16 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
struct saa7146_vv *vv = dev->vv_data;
spin_lock(&dev->slock);
- if (vv->vbi_q.curr) {
- DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_q.curr);
+ if (vv->vbi_dmaq.curr) {
+ DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr);
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
- vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
- saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE);
+ vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount;
+ saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
} else {
DEB_VBI("dev:%p\n", dev);
}
- saa7146_buffer_next(dev,&vv->vbi_q,1);
+ saa7146_buffer_next(dev, &vv->vbi_dmaq, 1);
spin_unlock(&dev->slock);
}
@@ -488,7 +474,7 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff
return -EBUSY;
}
- mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1,
file->f_flags & O_NONBLOCK);
/*
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index ce30533fd97..6d14785d474 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -2,6 +2,8 @@
#include <media/saa7146_vv.h>
#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
#include <linux/module.h>
static int max_memory = 32;
@@ -112,8 +114,8 @@ int saa7146_start_preview(struct saa7146_fh *fh)
DEB_EE("dev:%p, fh:%p\n", dev, fh);
- /* check if we have overlay informations */
- if( NULL == fh->ov.fh ) {
+ /* check if we have overlay information */
+ if (vv->ov.fh == NULL) {
DEB_D("no overlay data available. try S_FMT first.\n");
return -EAGAIN;
}
@@ -139,19 +141,18 @@ int saa7146_start_preview(struct saa7146_fh *fh)
return -EBUSY;
}
- fmt.fmt.win = fh->ov.win;
+ fmt.fmt.win = vv->ov.win;
err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
if (0 != err) {
saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
return -EBUSY;
}
- fh->ov.win = fmt.fmt.win;
- vv->ov_data = &fh->ov;
+ vv->ov.win = fmt.fmt.win;
DEB_D("%dx%d+%d+%d %s field=%s\n",
- fh->ov.win.w.width, fh->ov.win.w.height,
- fh->ov.win.w.left, fh->ov.win.w.top,
- vv->ov_fmt->name, v4l2_field_names[fh->ov.win.field]);
+ vv->ov.win.w.width, vv->ov.win.w.height,
+ vv->ov.win.w.left, vv->ov.win.w.top,
+ vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]);
if (0 != (ret = saa7146_enable_overlay(fh))) {
DEB_D("enabling overlay failed: %d\n", ret);
@@ -202,65 +203,6 @@ int saa7146_stop_preview(struct saa7146_fh *fh)
EXPORT_SYMBOL_GPL(saa7146_stop_preview);
/********************************************************************************/
-/* device controls */
-
-static struct v4l2_queryctrl controls[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- },{
- .id = V4L2_CID_CONTRAST,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- },{
- .id = V4L2_CID_SATURATION,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 127,
- .step = 1,
- .default_value = 64,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .flags = V4L2_CTRL_FLAG_SLIDER,
- },{
- .id = V4L2_CID_VFLIP,
- .name = "Vertical Flip",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_HFLIP,
- .name = "Horizontal Flip",
- .minimum = 0,
- .maximum = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },
-};
-static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl);
-
-#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0)
-
-static struct v4l2_queryctrl* ctrl_by_id(int id)
-{
- int i;
-
- for (i = 0; i < NUM_CONTROLS; i++)
- if (controls[i].id == id)
- return controls+i;
- return NULL;
-}
-
-/********************************************************************************/
/* common pagetable functions */
static int saa7146_pgtable_build(struct saa7146_dev *dev, struct saa7146_buf *buf)
@@ -413,7 +355,7 @@ static int video_begin(struct saa7146_fh *fh)
}
}
- fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat);
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
/* we need to have a valid format set here */
BUG_ON(NULL == fmt);
@@ -465,7 +407,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
return -EBUSY;
}
- fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat);
+ fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat);
/* we need to have a valid format set here */
BUG_ON(NULL == fmt);
@@ -504,18 +446,25 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
{
+ struct video_device *vdev = video_devdata(file);
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
strcpy((char *)cap->driver, "saa7146 v4l2");
strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci));
- cap->version = SAA7146_VERSION_CODE;
- cap->capabilities =
+ cap->device_caps =
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_VIDEO_OVERLAY |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
- cap->capabilities |= dev->ext_vv_data->capabilities;
+ cap->device_caps |= dev->ext_vv_data->capabilities;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ if (vdev->vfl_type == VFL_TYPE_GRABBER)
+ cap->device_caps &=
+ ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT);
+ else
+ cap->device_caps &=
+ ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO);
return 0;
}
@@ -526,6 +475,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f
*fb = vv->ov_fb;
fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+ fb->flags = V4L2_FBUF_FLAG_PRIMARY;
return 0;
}
@@ -579,135 +529,58 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtd
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+int saa7146_s_ctrl(struct v4l2_ctrl *ctrl)
{
- const struct v4l2_queryctrl *ctrl;
-
- if ((c->id < V4L2_CID_BASE ||
- c->id >= V4L2_CID_LASTP1) &&
- (c->id < V4L2_CID_PRIVATE_BASE ||
- c->id >= V4L2_CID_PRIVATE_LASTP1))
- return -EINVAL;
-
- ctrl = ctrl_by_id(c->id);
- if (ctrl == NULL)
- return -EINVAL;
-
- DEB_EE("VIDIOC_QUERYCTRL: id:%d\n", c->id);
- *c = *ctrl;
- return 0;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
struct saa7146_vv *vv = dev->vv_data;
- const struct v4l2_queryctrl *ctrl;
- u32 value = 0;
+ u32 val;
- ctrl = ctrl_by_id(c->id);
- if (NULL == ctrl)
- return -EINVAL;
- switch (c->id) {
+ switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- value = saa7146_read(dev, BCS_CTRL);
- c->value = 0xff & (value >> 24);
- DEB_D("V4L2_CID_BRIGHTNESS: %d\n", c->value);
- break;
- case V4L2_CID_CONTRAST:
- value = saa7146_read(dev, BCS_CTRL);
- c->value = 0x7f & (value >> 16);
- DEB_D("V4L2_CID_CONTRAST: %d\n", c->value);
- break;
- case V4L2_CID_SATURATION:
- value = saa7146_read(dev, BCS_CTRL);
- c->value = 0x7f & (value >> 0);
- DEB_D("V4L2_CID_SATURATION: %d\n", c->value);
- break;
- case V4L2_CID_VFLIP:
- c->value = vv->vflip;
- DEB_D("V4L2_CID_VFLIP: %d\n", c->value);
- break;
- case V4L2_CID_HFLIP:
- c->value = vv->hflip;
- DEB_D("V4L2_CID_HFLIP: %d\n", c->value);
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct saa7146_vv *vv = dev->vv_data;
- const struct v4l2_queryctrl *ctrl;
-
- ctrl = ctrl_by_id(c->id);
- if (NULL == ctrl) {
- DEB_D("unknown control %d\n", c->id);
- return -EINVAL;
- }
-
- switch (ctrl->type) {
- case V4L2_CTRL_TYPE_BOOLEAN:
- case V4L2_CTRL_TYPE_MENU:
- case V4L2_CTRL_TYPE_INTEGER:
- if (c->value < ctrl->minimum)
- c->value = ctrl->minimum;
- if (c->value > ctrl->maximum)
- c->value = ctrl->maximum;
- break;
- default:
- /* nothing */;
- }
-
- switch (c->id) {
- case V4L2_CID_BRIGHTNESS: {
- u32 value = saa7146_read(dev, BCS_CTRL);
- value &= 0x00ffffff;
- value |= (c->value << 24);
- saa7146_write(dev, BCS_CTRL, value);
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0x00ffffff;
+ val |= (ctrl->val << 24);
+ saa7146_write(dev, BCS_CTRL, val);
saa7146_write(dev, MC2, MASK_22 | MASK_06);
break;
- }
- case V4L2_CID_CONTRAST: {
- u32 value = saa7146_read(dev, BCS_CTRL);
- value &= 0xff00ffff;
- value |= (c->value << 16);
- saa7146_write(dev, BCS_CTRL, value);
+
+ case V4L2_CID_CONTRAST:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xff00ffff;
+ val |= (ctrl->val << 16);
+ saa7146_write(dev, BCS_CTRL, val);
saa7146_write(dev, MC2, MASK_22 | MASK_06);
break;
- }
- case V4L2_CID_SATURATION: {
- u32 value = saa7146_read(dev, BCS_CTRL);
- value &= 0xffffff00;
- value |= (c->value << 0);
- saa7146_write(dev, BCS_CTRL, value);
+
+ case V4L2_CID_SATURATION:
+ val = saa7146_read(dev, BCS_CTRL);
+ val &= 0xffffff00;
+ val |= (ctrl->val << 0);
+ saa7146_write(dev, BCS_CTRL, val);
saa7146_write(dev, MC2, MASK_22 | MASK_06);
break;
- }
+
case V4L2_CID_HFLIP:
/* fixme: we can support changing VFLIP and HFLIP here... */
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D("V4L2_CID_HFLIP while active capture\n");
+ if ((vv->video_status & STATUS_CAPTURE))
return -EBUSY;
- }
- vv->hflip = c->value;
+ vv->hflip = ctrl->val;
break;
+
case V4L2_CID_VFLIP:
- if (IS_CAPTURE_ACTIVE(fh) != 0) {
- DEB_D("V4L2_CID_VFLIP while active capture\n");
+ if ((vv->video_status & STATUS_CAPTURE))
return -EBUSY;
- }
- vv->vflip = c->value;
+ vv->vflip = ctrl->val;
break;
+
default:
return -EINVAL;
}
- if (IS_OVERLAY_ACTIVE(fh) != 0) {
+ if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */
+ struct saa7146_fh *fh = vv->video_fh;
+
saa7146_stop_preview(fh);
saa7146_start_preview(fh);
}
@@ -720,6 +593,8 @@ static int vidioc_g_parm(struct file *file, void *fh,
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct saa7146_vv *vv = dev->vv_data;
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
parm->parm.capture.readbuffers = 1;
v4l2_video_std_frame_period(vv->standard->id,
&parm->parm.capture.timeperframe);
@@ -728,19 +603,28 @@ static int vidioc_g_parm(struct file *file, void *fh,
static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
{
- f->fmt.pix = ((struct saa7146_fh *)fh)->video_fmt;
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.pix = vv->video_fmt;
return 0;
}
static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
{
- f->fmt.win = ((struct saa7146_fh *)fh)->ov.win;
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.win = vv->ov.win;
return 0;
}
static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
{
- f->fmt.vbi = ((struct saa7146_fh *)fh)->vbi_fmt;
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
+
+ f->fmt.vbi = vv->vbi_fmt;
return 0;
}
@@ -787,6 +671,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
}
f->fmt.pix.field = field;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
if (f->fmt.pix.width > maxw)
f->fmt.pix.width = maxw;
if (f->fmt.pix.height > maxh)
@@ -883,9 +768,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_forma
err = vidioc_try_fmt_vid_cap(file, fh, f);
if (0 != err)
return err;
- fh->video_fmt = f->fmt.pix;
+ vv->video_fmt = f->fmt.pix;
DEB_EE("set to pixelformat '%4.4s'\n",
- (char *)&fh->video_fmt.pixelformat);
+ (char *)&vv->video_fmt.pixelformat);
return 0;
}
@@ -900,17 +785,17 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f
err = vidioc_try_fmt_vid_overlay(file, fh, f);
if (0 != err)
return err;
- fh->ov.win = f->fmt.win;
- fh->ov.nclips = f->fmt.win.clipcount;
- if (fh->ov.nclips > 16)
- fh->ov.nclips = 16;
- if (copy_from_user(fh->ov.clips, f->fmt.win.clips,
- sizeof(struct v4l2_clip) * fh->ov.nclips)) {
+ vv->ov.win = f->fmt.win;
+ vv->ov.nclips = f->fmt.win.clipcount;
+ if (vv->ov.nclips > 16)
+ vv->ov.nclips = 16;
+ if (copy_from_user(vv->ov.clips, f->fmt.win.clips,
+ sizeof(struct v4l2_clip) * vv->ov.nclips)) {
return -EFAULT;
}
- /* fh->ov.fh is used to indicate that we have valid overlay informations, too */
- fh->ov.fh = fh;
+ /* vv->ov.fh is used to indicate that we have valid overlay informations, too */
+ vv->ov.fh = fh;
/* check if our current overlay is active */
if (IS_OVERLAY_ACTIVE(fh) != 0) {
@@ -1111,10 +996,14 @@ static int vidioc_g_chip_ident(struct file *file, void *__fh,
chip->ident = V4L2_IDENT_NONE;
chip->revision = 0;
- if (chip->match.type == V4L2_CHIP_MATCH_HOST && !chip->match.addr) {
- chip->ident = V4L2_IDENT_SAA7146;
+ if (chip->match.type == V4L2_CHIP_MATCH_HOST) {
+ if (v4l2_chip_match_host(&chip->match))
+ chip->ident = V4L2_IDENT_SAA7146;
return 0;
}
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
return v4l2_device_call_until_err(&dev->v4l2_dev, 0,
core, g_chip_ident, chip);
}
@@ -1129,7 +1018,6 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
.vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
.vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
- .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
.vidioc_g_chip_ident = vidioc_g_chip_ident,
.vidioc_overlay = vidioc_overlay,
@@ -1141,12 +1029,29 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
.vidioc_dqbuf = vidioc_dqbuf,
.vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/*********************************************************************************/
@@ -1161,7 +1066,7 @@ static int buffer_activate (struct saa7146_dev *dev,
buf->vb.state = VIDEOBUF_ACTIVE;
saa7146_set_capture(dev,buf,next);
- mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT);
+ mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
@@ -1185,44 +1090,44 @@ static int buffer_prepare(struct videobuf_queue *q,
DEB_CAP("vbuf:%p\n", vb);
/* sanity checks */
- if (fh->video_fmt.width < 48 ||
- fh->video_fmt.height < 32 ||
- fh->video_fmt.width > vv->standard->h_max_out ||
- fh->video_fmt.height > vv->standard->v_max_out) {
+ if (vv->video_fmt.width < 48 ||
+ vv->video_fmt.height < 32 ||
+ vv->video_fmt.width > vv->standard->h_max_out ||
+ vv->video_fmt.height > vv->standard->v_max_out) {
DEB_D("w (%d) / h (%d) out of bounds\n",
- fh->video_fmt.width, fh->video_fmt.height);
+ vv->video_fmt.width, vv->video_fmt.height);
return -EINVAL;
}
- size = fh->video_fmt.sizeimage;
+ size = vv->video_fmt.sizeimage;
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
DEB_D("size mismatch\n");
return -EINVAL;
}
DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n",
- fh->video_fmt.width, fh->video_fmt.height,
- size, v4l2_field_names[fh->video_fmt.field]);
- if (buf->vb.width != fh->video_fmt.width ||
- buf->vb.bytesperline != fh->video_fmt.bytesperline ||
- buf->vb.height != fh->video_fmt.height ||
+ vv->video_fmt.width, vv->video_fmt.height,
+ size, v4l2_field_names[vv->video_fmt.field]);
+ if (buf->vb.width != vv->video_fmt.width ||
+ buf->vb.bytesperline != vv->video_fmt.bytesperline ||
+ buf->vb.height != vv->video_fmt.height ||
buf->vb.size != size ||
buf->vb.field != field ||
- buf->vb.field != fh->video_fmt.field ||
- buf->fmt != &fh->video_fmt) {
+ buf->vb.field != vv->video_fmt.field ||
+ buf->fmt != &vv->video_fmt) {
saa7146_dma_free(dev,q,buf);
}
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct saa7146_format *sfmt;
- buf->vb.bytesperline = fh->video_fmt.bytesperline;
- buf->vb.width = fh->video_fmt.width;
- buf->vb.height = fh->video_fmt.height;
+ buf->vb.bytesperline = vv->video_fmt.bytesperline;
+ buf->vb.width = vv->video_fmt.width;
+ buf->vb.height = vv->video_fmt.height;
buf->vb.size = size;
buf->vb.field = field;
- buf->fmt = &fh->video_fmt;
- buf->vb.field = fh->video_fmt.field;
+ buf->fmt = &vv->video_fmt;
+ buf->vb.field = vv->video_fmt.field;
sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat);
@@ -1258,11 +1163,12 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned
{
struct file *file = q->priv_data;
struct saa7146_fh *fh = file->private_data;
+ struct saa7146_vv *vv = fh->dev->vv_data;
if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS)
*count = MAX_SAA7146_CAPTURE_BUFFERS;
- *size = fh->video_fmt.sizeimage;
+ *size = vv->video_fmt.sizeimage;
/* check if we exceed the "max_memory" parameter */
if( (*count * *size) > (max_memory*1048576) ) {
@@ -1283,7 +1189,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_CAP("vbuf:%p\n", vb);
- saa7146_buffer_queue(fh->dev,&vv->video_q,buf);
+ saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf);
}
static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
@@ -1312,12 +1218,12 @@ static struct videobuf_queue_ops video_qops = {
static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
{
- INIT_LIST_HEAD(&vv->video_q.queue);
+ INIT_LIST_HEAD(&vv->video_dmaq.queue);
- init_timer(&vv->video_q.timeout);
- vv->video_q.timeout.function = saa7146_buffer_timeout;
- vv->video_q.timeout.data = (unsigned long)(&vv->video_q);
- vv->video_q.dev = dev;
+ init_timer(&vv->video_dmaq.timeout);
+ vv->video_dmaq.timeout.function = saa7146_buffer_timeout;
+ vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq);
+ vv->video_dmaq.dev = dev;
/* set some default values */
vv->standard = &dev->ext_vv_data->stds[0];
@@ -1331,15 +1237,6 @@ static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
static int video_open(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = file->private_data;
- struct saa7146_format *sfmt;
-
- fh->video_fmt.width = 384;
- fh->video_fmt.height = 288;
- fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24;
- fh->video_fmt.bytesperline = 0;
- fh->video_fmt.field = V4L2_FIELD_ANY;
- sfmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat);
- fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8;
videobuf_queue_sg_init(&fh->video_q, &video_qops,
&dev->pci->dev, &dev->slock,
@@ -1371,7 +1268,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file)
static void video_irq_done(struct saa7146_dev *dev, unsigned long st)
{
struct saa7146_vv *vv = dev->vv_data;
- struct saa7146_dmaqueue *q = &vv->video_q;
+ struct saa7146_dmaqueue *q = &vv->video_dmaq;
spin_lock(&dev->slock);
DEB_CAP("called\n");
diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
index 4a6d5cef396..bbf4945149a 100644
--- a/drivers/media/common/tuners/Kconfig
+++ b/drivers/media/common/tuners/Kconfig
@@ -204,6 +204,27 @@ config MEDIA_TUNER_TDA18218
help
NXP TDA18218 silicon tuner driver.
+config MEDIA_TUNER_FC0011
+ tristate "Fitipower FC0011 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Fitipower FC0011 silicon tuner driver.
+
+config MEDIA_TUNER_FC0012
+ tristate "Fitipower FC0012 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Fitipower FC0012 silicon tuner driver.
+
+config MEDIA_TUNER_FC0013
+ tristate "Fitipower FC0013 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Fitipower FC0013 silicon tuner driver.
+
config MEDIA_TUNER_TDA18212
tristate "NXP TDA18212 silicon tuner"
depends on VIDEO_MEDIA && I2C
@@ -211,4 +232,10 @@ config MEDIA_TUNER_TDA18212
help
NXP TDA18212 silicon tuner driver.
+config MEDIA_TUNER_TUA9001
+ tristate "Infineon TUA 9001 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Infineon TUA 9001 silicon tuner driver.
endmenu
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index f80407eb899..891b80e6080 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -28,6 +28,10 @@ obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
+obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o
+obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o
+obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o
+obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o
ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c
new file mode 100644
index 00000000000..e4882546c28
--- /dev/null
+++ b/drivers/media/common/tuners/fc0011.c
@@ -0,0 +1,524 @@
+/*
+ * Fitipower FC0011 tuner driver
+ *
+ * Copyright (C) 2012 Michael Buesch <m@bues.ch>
+ *
+ * Derived from FC0012 tuner driver:
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 "fc0011.h"
+
+
+/* Tuner registers */
+enum {
+ FC11_REG_0,
+ FC11_REG_FA, /* FA */
+ FC11_REG_FP, /* FP */
+ FC11_REG_XINHI, /* XIN high 8 bit */
+ FC11_REG_XINLO, /* XIN low 8 bit */
+ FC11_REG_VCO, /* VCO */
+ FC11_REG_VCOSEL, /* VCO select */
+ FC11_REG_7, /* Unknown tuner reg 7 */
+ FC11_REG_8, /* Unknown tuner reg 8 */
+ FC11_REG_9,
+ FC11_REG_10, /* Unknown tuner reg 10 */
+ FC11_REG_11, /* Unknown tuner reg 11 */
+ FC11_REG_12,
+ FC11_REG_RCCAL, /* RC calibrate */
+ FC11_REG_VCOCAL, /* VCO calibrate */
+ FC11_REG_15,
+ FC11_REG_16, /* Unknown tuner reg 16 */
+ FC11_REG_17,
+
+ FC11_NR_REGS, /* Number of registers */
+};
+
+enum FC11_REG_VCOSEL_bits {
+ FC11_VCOSEL_2 = 0x08, /* VCO select 2 */
+ FC11_VCOSEL_1 = 0x10, /* VCO select 1 */
+ FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */
+ FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */
+ FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */
+};
+
+enum FC11_REG_RCCAL_bits {
+ FC11_RCCAL_FORCE = 0x10, /* force */
+};
+
+enum FC11_REG_VCOCAL_bits {
+ FC11_VCOCAL_RUN = 0, /* VCO calibration run */
+ FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */
+ FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */
+ FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */
+};
+
+
+struct fc0011_priv {
+ struct i2c_adapter *i2c;
+ u8 addr;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+
+static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = { .addr = priv->addr,
+ .flags = 0, .buf = buf, .len = 2 };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ dev_err(&priv->i2c->dev,
+ "I2C write reg failed, reg: %02x, val: %02x\n",
+ reg, val);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val)
+{
+ u8 dummy;
+ struct i2c_msg msg[2] = {
+ { .addr = priv->addr,
+ .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->addr,
+ .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ dev_err(&priv->i2c->dev,
+ "I2C read failed, reg: %02x\n", reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int fc0011_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static int fc0011_init(struct dvb_frontend *fe)
+{
+ struct fc0011_priv *priv = fe->tuner_priv;
+ int err;
+
+ if (WARN_ON(!fe->callback))
+ return -EINVAL;
+
+ err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ FC0011_FE_CALLBACK_POWER, priv->addr);
+ if (err) {
+ dev_err(&priv->i2c->dev, "Power-on callback failed\n");
+ return err;
+ }
+ err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ FC0011_FE_CALLBACK_RESET, priv->addr);
+ if (err) {
+ dev_err(&priv->i2c->dev, "Reset callback failed\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/* Initiate VCO calibration */
+static int fc0011_vcocal_trigger(struct fc0011_priv *priv)
+{
+ int err;
+
+ err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET);
+ if (err)
+ return err;
+ err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/* Read VCO calibration value */
+static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value)
+{
+ int err;
+
+ err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN);
+ if (err)
+ return err;
+ usleep_range(10000, 20000);
+ err = fc0011_readreg(priv, FC11_REG_VCOCAL, value);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int fc0011_set_params(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct fc0011_priv *priv = fe->tuner_priv;
+ int err;
+ unsigned int i, vco_retries;
+ u32 freq = p->frequency / 1000;
+ u32 bandwidth = p->bandwidth_hz / 1000;
+ u32 fvco, xin, xdiv, xdivr;
+ u16 frac;
+ u8 fa, fp, vco_sel, vco_cal;
+ u8 regs[FC11_NR_REGS] = { };
+
+ regs[FC11_REG_7] = 0x0F;
+ regs[FC11_REG_8] = 0x3E;
+ regs[FC11_REG_10] = 0xB8;
+ regs[FC11_REG_11] = 0x80;
+ regs[FC11_REG_RCCAL] = 0x04;
+ err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
+ err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
+ err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
+ err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
+ err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
+ if (err)
+ return -EIO;
+
+ /* Set VCO freq and VCO div */
+ if (freq < 54000) {
+ fvco = freq * 64;
+ regs[FC11_REG_VCO] = 0x82;
+ } else if (freq < 108000) {
+ fvco = freq * 32;
+ regs[FC11_REG_VCO] = 0x42;
+ } else if (freq < 216000) {
+ fvco = freq * 16;
+ regs[FC11_REG_VCO] = 0x22;
+ } else if (freq < 432000) {
+ fvco = freq * 8;
+ regs[FC11_REG_VCO] = 0x12;
+ } else {
+ fvco = freq * 4;
+ regs[FC11_REG_VCO] = 0x0A;
+ }
+
+ /* Calc XIN. The PLL reference frequency is 18 MHz. */
+ xdiv = fvco / 18000;
+ frac = fvco - xdiv * 18000;
+ frac = (frac << 15) / 18000;
+ if (frac >= 16384)
+ frac += 32786;
+ if (!frac)
+ xin = 0;
+ else if (frac < 511)
+ xin = 512;
+ else if (frac < 65026)
+ xin = frac;
+ else
+ xin = 65024;
+ regs[FC11_REG_XINHI] = xin >> 8;
+ regs[FC11_REG_XINLO] = xin;
+
+ /* Calc FP and FA */
+ xdivr = xdiv;
+ if (fvco - xdiv * 18000 >= 9000)
+ xdivr += 1; /* round */
+ fp = xdivr / 8;
+ fa = xdivr - fp * 8;
+ if (fa < 2) {
+ fp -= 1;
+ fa += 8;
+ }
+ if (fp > 0x1F) {
+ fp &= 0x1F;
+ fa &= 0xF;
+ }
+ if (fa >= fp) {
+ dev_warn(&priv->i2c->dev,
+ "fa %02X >= fp %02X, but trying to continue\n",
+ (unsigned int)(u8)fa, (unsigned int)(u8)fp);
+ }
+ regs[FC11_REG_FA] = fa;
+ regs[FC11_REG_FP] = fp;
+
+ /* Select bandwidth */
+ switch (bandwidth) {
+ case 8000:
+ break;
+ case 7000:
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M;
+ break;
+ default:
+ dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. "
+ "Using 6000 kHz.\n",
+ bandwidth);
+ bandwidth = 6000;
+ /* fallthrough */
+ case 6000:
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M;
+ break;
+ }
+
+ /* Pre VCO select */
+ if (fvco < 2320000) {
+ vco_sel = 0;
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ } else if (fvco < 3080000) {
+ vco_sel = 1;
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+ } else {
+ vco_sel = 2;
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
+ }
+
+ /* Fix for low freqs */
+ if (freq < 45000) {
+ regs[FC11_REG_FA] = 0x6;
+ regs[FC11_REG_FP] = 0x11;
+ }
+
+ /* Clock out fix */
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT;
+
+ /* Write the cached registers */
+ for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) {
+ err = fc0011_writereg(priv, i, regs[i]);
+ if (err)
+ return err;
+ }
+
+ /* VCO calibration */
+ err = fc0011_vcocal_trigger(priv);
+ if (err)
+ return err;
+ err = fc0011_vcocal_read(priv, &vco_cal);
+ if (err)
+ return err;
+ vco_retries = 0;
+ while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) {
+ /* Reset the tuner and try again */
+ err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ FC0011_FE_CALLBACK_RESET, priv->addr);
+ if (err) {
+ dev_err(&priv->i2c->dev, "Failed to reset tuner\n");
+ return err;
+ }
+ /* Reinit tuner config */
+ err = 0;
+ for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++)
+ err |= fc0011_writereg(priv, i, regs[i]);
+ err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]);
+ err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]);
+ err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]);
+ err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]);
+ err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
+ if (err)
+ return -EIO;
+ /* VCO calibration */
+ err = fc0011_vcocal_trigger(priv);
+ if (err)
+ return err;
+ err = fc0011_vcocal_read(priv, &vco_cal);
+ if (err)
+ return err;
+ vco_retries++;
+ }
+ if (!(vco_cal & FC11_VCOCAL_OK)) {
+ dev_err(&priv->i2c->dev,
+ "Failed to read VCO calibration value (got %02X)\n",
+ (unsigned int)vco_cal);
+ return -EIO;
+ }
+ vco_cal &= FC11_VCOCAL_VALUEMASK;
+
+ switch (vco_sel) {
+ case 0:
+ if (vco_cal < 8) {
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+ err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+ regs[FC11_REG_VCOSEL]);
+ if (err)
+ return err;
+ err = fc0011_vcocal_trigger(priv);
+ if (err)
+ return err;
+ } else {
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+ regs[FC11_REG_VCOSEL]);
+ if (err)
+ return err;
+ }
+ break;
+ case 1:
+ if (vco_cal < 5) {
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
+ err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+ regs[FC11_REG_VCOSEL]);
+ if (err)
+ return err;
+ err = fc0011_vcocal_trigger(priv);
+ if (err)
+ return err;
+ } else if (vco_cal <= 48) {
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+ err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+ regs[FC11_REG_VCOSEL]);
+ if (err)
+ return err;
+ } else {
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+ regs[FC11_REG_VCOSEL]);
+ if (err)
+ return err;
+ err = fc0011_vcocal_trigger(priv);
+ if (err)
+ return err;
+ }
+ break;
+ case 2:
+ if (vco_cal > 53) {
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1;
+ err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+ regs[FC11_REG_VCOSEL]);
+ if (err)
+ return err;
+ err = fc0011_vcocal_trigger(priv);
+ if (err)
+ return err;
+ } else {
+ regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2);
+ regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2;
+ err = fc0011_writereg(priv, FC11_REG_VCOSEL,
+ regs[FC11_REG_VCOSEL]);
+ if (err)
+ return err;
+ }
+ break;
+ }
+ err = fc0011_vcocal_read(priv, NULL);
+ if (err)
+ return err;
+ usleep_range(10000, 50000);
+
+ err = fc0011_readreg(priv, FC11_REG_RCCAL, &regs[FC11_REG_RCCAL]);
+ if (err)
+ return err;
+ regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE;
+ err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]);
+ if (err)
+ return err;
+ err = fc0011_writereg(priv, FC11_REG_16, 0xB);
+ if (err)
+ return err;
+
+ dev_dbg(&priv->i2c->dev, "Tuned to "
+ "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X "
+ "vcocal=%02X(%u) bw=%u\n",
+ (unsigned int)regs[FC11_REG_FA],
+ (unsigned int)regs[FC11_REG_FP],
+ (unsigned int)regs[FC11_REG_XINHI],
+ (unsigned int)regs[FC11_REG_XINLO],
+ (unsigned int)regs[FC11_REG_VCO],
+ (unsigned int)regs[FC11_REG_VCOSEL],
+ (unsigned int)vco_cal, vco_retries,
+ (unsigned int)bandwidth);
+
+ priv->frequency = p->frequency;
+ priv->bandwidth = p->bandwidth_hz;
+
+ return 0;
+}
+
+static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct fc0011_priv *priv = fe->tuner_priv;
+
+ *frequency = priv->frequency;
+
+ return 0;
+}
+
+static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ *frequency = 0;
+
+ return 0;
+}
+
+static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct fc0011_priv *priv = fe->tuner_priv;
+
+ *bandwidth = priv->bandwidth;
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops fc0011_tuner_ops = {
+ .info = {
+ .name = "Fitipower FC0011",
+
+ .frequency_min = 45000000,
+ .frequency_max = 1000000000,
+ },
+
+ .release = fc0011_release,
+ .init = fc0011_init,
+
+ .set_params = fc0011_set_params,
+
+ .get_frequency = fc0011_get_frequency,
+ .get_if_frequency = fc0011_get_if_frequency,
+ .get_bandwidth = fc0011_get_bandwidth,
+};
+
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct fc0011_config *config)
+{
+ struct fc0011_priv *priv;
+
+ priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ priv->i2c = i2c;
+ priv->addr = config->i2c_address;
+
+ fe->tuner_priv = priv;
+ fe->ops.tuner_ops = fc0011_tuner_ops;
+
+ dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n");
+
+ return fe;
+}
+EXPORT_SYMBOL(fc0011_attach);
+
+MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver");
+MODULE_AUTHOR("Michael Buesch <m@bues.ch>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/common/tuners/fc0011.h
new file mode 100644
index 00000000000..0ee581f122d
--- /dev/null
+++ b/drivers/media/common/tuners/fc0011.h
@@ -0,0 +1,41 @@
+#ifndef LINUX_FC0011_H_
+#define LINUX_FC0011_H_
+
+#include "dvb_frontend.h"
+
+
+/** struct fc0011_config - fc0011 hardware config
+ *
+ * @i2c_address: I2C bus address.
+ */
+struct fc0011_config {
+ u8 i2c_address;
+};
+
+/** enum fc0011_fe_callback_commands - Frontend callbacks
+ *
+ * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware.
+ * @FC0011_FE_CALLBACK_RESET: Request a tuner reset.
+ */
+enum fc0011_fe_callback_commands {
+ FC0011_FE_CALLBACK_POWER,
+ FC0011_FE_CALLBACK_RESET,
+};
+
+#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\
+ defined(CONFIG_MEDIA_TUNER_FC0011_MODULE)
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct fc0011_config *config);
+#else
+static inline
+struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ const struct fc0011_config *config)
+{
+ dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n");
+ return NULL;
+}
+#endif
+
+#endif /* LINUX_FC0011_H_ */
diff --git a/drivers/media/common/tuners/fc0012-priv.h b/drivers/media/common/tuners/fc0012-priv.h
new file mode 100644
index 00000000000..4577c917e61
--- /dev/null
+++ b/drivers/media/common/tuners/fc0012-priv.h
@@ -0,0 +1,43 @@
+/*
+ * Fitipower FC0012 tuner driver - private includes
+ *
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _FC0012_PRIV_H_
+#define _FC0012_PRIV_H_
+
+#define LOG_PREFIX "fc0012"
+
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct fc0012_priv {
+ struct i2c_adapter *i2c;
+ u8 addr;
+ u8 dual_master;
+ u8 xtal_freq;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+#endif
diff --git a/drivers/media/common/tuners/fc0012.c b/drivers/media/common/tuners/fc0012.c
new file mode 100644
index 00000000000..308135abd54
--- /dev/null
+++ b/drivers/media/common/tuners/fc0012.c
@@ -0,0 +1,467 @@
+/*
+ * Fitipower FC0012 tuner driver
+ *
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 "fc0012.h"
+#include "fc0012-priv.h"
+
+static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = {reg, val};
+ struct i2c_msg msg = {
+ .addr = priv->addr, .flags = 0, .buf = buf, .len = 2
+ };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ err("I2C write reg failed, reg: %02x, val: %02x", reg, val);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->addr, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ err("I2C read reg failed, reg: %02x", reg);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int fc0012_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int fc0012_init(struct dvb_frontend *fe)
+{
+ struct fc0012_priv *priv = fe->tuner_priv;
+ int i, ret = 0;
+ unsigned char reg[] = {
+ 0x00, /* dummy reg. 0 */
+ 0x05, /* reg. 0x01 */
+ 0x10, /* reg. 0x02 */
+ 0x00, /* reg. 0x03 */
+ 0x00, /* reg. 0x04 */
+ 0x0f, /* reg. 0x05: may also be 0x0a */
+ 0x00, /* reg. 0x06: divider 2, VCO slow */
+ 0x00, /* reg. 0x07: may also be 0x0f */
+ 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
+ Loop Bw 1/8 */
+ 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */
+ 0xb8, /* reg. 0x0a: Disable LO Test Buffer */
+ 0x82, /* reg. 0x0b: Output Clock is same as clock frequency,
+ may also be 0x83 */
+ 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
+ 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */
+ 0x00, /* reg. 0x0e */
+ 0x00, /* reg. 0x0f */
+ 0x00, /* reg. 0x10: may also be 0x0d */
+ 0x00, /* reg. 0x11 */
+ 0x1f, /* reg. 0x12: Set to maximum gain */
+ 0x08, /* reg. 0x13: Set to Middle Gain: 0x08,
+ Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */
+ 0x00, /* reg. 0x14 */
+ 0x04, /* reg. 0x15: Enable LNA COMPS */
+ };
+
+ switch (priv->xtal_freq) {
+ case FC_XTAL_27_MHZ:
+ case FC_XTAL_28_8_MHZ:
+ reg[0x07] |= 0x20;
+ break;
+ case FC_XTAL_36_MHZ:
+ default:
+ break;
+ }
+
+ if (priv->dual_master)
+ reg[0x0c] |= 0x02;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ for (i = 1; i < sizeof(reg); i++) {
+ ret = fc0012_writereg(priv, i, reg[i]);
+ if (ret)
+ break;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ if (ret)
+ err("fc0012_writereg failed: %d", ret);
+
+ return ret;
+}
+
+static int fc0012_sleep(struct dvb_frontend *fe)
+{
+ /* nothing to do here */
+ return 0;
+}
+
+static int fc0012_set_params(struct dvb_frontend *fe)
+{
+ struct fc0012_priv *priv = fe->tuner_priv;
+ int i, ret = 0;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 freq = p->frequency / 1000;
+ u32 delsys = p->delivery_system;
+ unsigned char reg[7], am, pm, multi, tmp;
+ unsigned long f_vco;
+ unsigned short xtal_freq_khz_2, xin, xdiv;
+ int vco_select = false;
+
+ if (fe->callback) {
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1));
+ if (ret)
+ goto exit;
+ }
+
+ switch (priv->xtal_freq) {
+ case FC_XTAL_27_MHZ:
+ xtal_freq_khz_2 = 27000 / 2;
+ break;
+ case FC_XTAL_36_MHZ:
+ xtal_freq_khz_2 = 36000 / 2;
+ break;
+ case FC_XTAL_28_8_MHZ:
+ default:
+ xtal_freq_khz_2 = 28800 / 2;
+ break;
+ }
+
+ /* select frequency divider and the frequency of VCO */
+ if (freq < 37084) { /* freq * 96 < 3560000 */
+ multi = 96;
+ reg[5] = 0x82;
+ reg[6] = 0x00;
+ } else if (freq < 55625) { /* freq * 64 < 3560000 */
+ multi = 64;
+ reg[5] = 0x82;
+ reg[6] = 0x02;
+ } else if (freq < 74167) { /* freq * 48 < 3560000 */
+ multi = 48;
+ reg[5] = 0x42;
+ reg[6] = 0x00;
+ } else if (freq < 111250) { /* freq * 32 < 3560000 */
+ multi = 32;
+ reg[5] = 0x42;
+ reg[6] = 0x02;
+ } else if (freq < 148334) { /* freq * 24 < 3560000 */
+ multi = 24;
+ reg[5] = 0x22;
+ reg[6] = 0x00;
+ } else if (freq < 222500) { /* freq * 16 < 3560000 */
+ multi = 16;
+ reg[5] = 0x22;
+ reg[6] = 0x02;
+ } else if (freq < 296667) { /* freq * 12 < 3560000 */
+ multi = 12;
+ reg[5] = 0x12;
+ reg[6] = 0x00;
+ } else if (freq < 445000) { /* freq * 8 < 3560000 */
+ multi = 8;
+ reg[5] = 0x12;
+ reg[6] = 0x02;
+ } else if (freq < 593334) { /* freq * 6 < 3560000 */
+ multi = 6;
+ reg[5] = 0x0a;
+ reg[6] = 0x00;
+ } else {
+ multi = 4;
+ reg[5] = 0x0a;
+ reg[6] = 0x02;
+ }
+
+ f_vco = freq * multi;
+
+ if (f_vco >= 3060000) {
+ reg[6] |= 0x08;
+ vco_select = true;
+ }
+
+ if (freq >= 45000) {
+ /* From divided value (XDIV) determined the FA and FP value */
+ xdiv = (unsigned short)(f_vco / xtal_freq_khz_2);
+ if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2))
+ xdiv++;
+
+ pm = (unsigned char)(xdiv / 8);
+ am = (unsigned char)(xdiv - (8 * pm));
+
+ if (am < 2) {
+ reg[1] = am + 8;
+ reg[2] = pm - 1;
+ } else {
+ reg[1] = am;
+ reg[2] = pm;
+ }
+ } else {
+ /* fix for frequency less than 45 MHz */
+ reg[1] = 0x06;
+ reg[2] = 0x11;
+ }
+
+ /* fix clock out */
+ reg[6] |= 0x20;
+
+ /* From VCO frequency determines the XIN ( fractional part of Delta
+ Sigma PLL) and divided value (XDIV) */
+ xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2);
+ xin = (xin << 15) / xtal_freq_khz_2;
+ if (xin >= 16384)
+ xin += 32768;
+
+ reg[3] = xin >> 8; /* xin with 9 bit resolution */
+ reg[4] = xin & 0xff;
+
+ if (delsys == SYS_DVBT) {
+ reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */
+ switch (p->bandwidth_hz) {
+ case 6000000:
+ reg[6] |= 0x80;
+ break;
+ case 7000000:
+ reg[6] |= 0x40;
+ break;
+ case 8000000:
+ default:
+ break;
+ }
+ } else {
+ err("%s: modulation type not supported!", __func__);
+ return -EINVAL;
+ }
+
+ /* modified for Realtek demod */
+ reg[5] |= 0x07;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ for (i = 1; i <= 6; i++) {
+ ret = fc0012_writereg(priv, i, reg[i]);
+ if (ret)
+ goto exit;
+ }
+
+ /* VCO Calibration */
+ ret = fc0012_writereg(priv, 0x0e, 0x80);
+ if (!ret)
+ ret = fc0012_writereg(priv, 0x0e, 0x00);
+
+ /* VCO Re-Calibration if needed */
+ if (!ret)
+ ret = fc0012_writereg(priv, 0x0e, 0x00);
+
+ if (!ret) {
+ msleep(10);
+ ret = fc0012_readreg(priv, 0x0e, &tmp);
+ }
+ if (ret)
+ goto exit;
+
+ /* vco selection */
+ tmp &= 0x3f;
+
+ if (vco_select) {
+ if (tmp > 0x3c) {
+ reg[6] &= ~0x08;
+ ret = fc0012_writereg(priv, 0x06, reg[6]);
+ if (!ret)
+ ret = fc0012_writereg(priv, 0x0e, 0x80);
+ if (!ret)
+ ret = fc0012_writereg(priv, 0x0e, 0x00);
+ }
+ } else {
+ if (tmp < 0x02) {
+ reg[6] |= 0x08;
+ ret = fc0012_writereg(priv, 0x06, reg[6]);
+ if (!ret)
+ ret = fc0012_writereg(priv, 0x0e, 0x80);
+ if (!ret)
+ ret = fc0012_writereg(priv, 0x0e, 0x00);
+ }
+ }
+
+ priv->frequency = p->frequency;
+ priv->bandwidth = p->bandwidth_hz;
+
+exit:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+ if (ret)
+ warn("%s: failed: %d", __func__, ret);
+ return ret;
+}
+
+static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct fc0012_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ /* CHECK: always ? */
+ *frequency = 0;
+ return 0;
+}
+
+static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct fc0012_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+#define INPUT_ADC_LEVEL -8
+
+static int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct fc0012_priv *priv = fe->tuner_priv;
+ int ret;
+ unsigned char tmp;
+ int int_temp, lna_gain, int_lna, tot_agc_gain, power;
+ const int fc0012_lna_gain_table[] = {
+ /* low gain */
+ -63, -58, -99, -73,
+ -63, -65, -54, -60,
+ /* middle gain */
+ 71, 70, 68, 67,
+ 65, 63, 61, 58,
+ /* high gain */
+ 197, 191, 188, 186,
+ 184, 182, 181, 179,
+ };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ ret = fc0012_writereg(priv, 0x12, 0x00);
+ if (ret)
+ goto err;
+
+ ret = fc0012_readreg(priv, 0x12, &tmp);
+ if (ret)
+ goto err;
+ int_temp = tmp;
+
+ ret = fc0012_readreg(priv, 0x13, &tmp);
+ if (ret)
+ goto err;
+ lna_gain = tmp & 0x1f;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) {
+ int_lna = fc0012_lna_gain_table[lna_gain];
+ tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 +
+ (int_temp & 0x1f)) * 2;
+ power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10;
+
+ if (power >= 45)
+ *strength = 255; /* 100% */
+ else if (power < -95)
+ *strength = 0;
+ else
+ *strength = (power + 95) * 255 / 140;
+
+ *strength |= *strength << 8;
+ } else {
+ ret = -1;
+ }
+
+ goto exit;
+
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+exit:
+ if (ret)
+ warn("%s: failed: %d", __func__, ret);
+ return ret;
+}
+
+static const struct dvb_tuner_ops fc0012_tuner_ops = {
+ .info = {
+ .name = "Fitipower FC0012",
+
+ .frequency_min = 37000000, /* estimate */
+ .frequency_max = 862000000, /* estimate */
+ .frequency_step = 0,
+ },
+
+ .release = fc0012_release,
+
+ .init = fc0012_init,
+ .sleep = fc0012_sleep,
+
+ .set_params = fc0012_set_params,
+
+ .get_frequency = fc0012_get_frequency,
+ .get_if_frequency = fc0012_get_if_frequency,
+ .get_bandwidth = fc0012_get_bandwidth,
+
+ .get_rf_strength = fc0012_get_rf_strength,
+};
+
+struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 i2c_address, int dual_master,
+ enum fc001x_xtal_freq xtal_freq)
+{
+ struct fc0012_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c = i2c;
+ priv->dual_master = dual_master;
+ priv->addr = i2c_address;
+ priv->xtal_freq = xtal_freq;
+
+ info("Fitipower FC0012 successfully attached.");
+
+ fe->tuner_priv = priv;
+
+ memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ return fe;
+}
+EXPORT_SYMBOL(fc0012_attach);
+
+MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver");
+MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.6");
diff --git a/drivers/media/common/tuners/fc0012.h b/drivers/media/common/tuners/fc0012.h
new file mode 100644
index 00000000000..4dbd5efe884
--- /dev/null
+++ b/drivers/media/common/tuners/fc0012.h
@@ -0,0 +1,44 @@
+/*
+ * Fitipower FC0012 tuner driver - include
+ *
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _FC0012_H_
+#define _FC0012_H_
+
+#include "dvb_frontend.h"
+#include "fc001x-common.h"
+
+#if defined(CONFIG_MEDIA_TUNER_FC0012) || \
+ (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE))
+extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ u8 i2c_address, int dual_master,
+ enum fc001x_xtal_freq xtal_freq);
+#else
+static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ u8 i2c_address, int dual_master,
+ enum fc001x_xtal_freq xtal_freq)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/fc0013-priv.h b/drivers/media/common/tuners/fc0013-priv.h
new file mode 100644
index 00000000000..bfd49dedea2
--- /dev/null
+++ b/drivers/media/common/tuners/fc0013-priv.h
@@ -0,0 +1,44 @@
+/*
+ * Fitipower FC0013 tuner driver
+ *
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _FC0013_PRIV_H_
+#define _FC0013_PRIV_H_
+
+#define LOG_PREFIX "fc0013"
+
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct fc0013_priv {
+ struct i2c_adapter *i2c;
+ u8 addr;
+ u8 dual_master;
+ u8 xtal_freq;
+
+ u32 frequency;
+ u32 bandwidth;
+};
+
+#endif
diff --git a/drivers/media/common/tuners/fc0013.c b/drivers/media/common/tuners/fc0013.c
new file mode 100644
index 00000000000..bd8f0f1e8f3
--- /dev/null
+++ b/drivers/media/common/tuners/fc0013.c
@@ -0,0 +1,634 @@
+/*
+ * Fitipower FC0013 tuner driver
+ *
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ * partially based on driver code from Fitipower
+ * Copyright (C) 2010 Fitipower Integrated Technology Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "fc0013.h"
+#include "fc0013-priv.h"
+
+static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = {reg, val};
+ struct i2c_msg msg = {
+ .addr = priv->addr, .flags = 0, .buf = buf, .len = 2
+ };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ err("I2C write reg failed, reg: %02x, val: %02x", reg, val);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->addr, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ err("I2C read reg failed, reg: %02x", reg);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int fc0013_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int fc0013_init(struct dvb_frontend *fe)
+{
+ struct fc0013_priv *priv = fe->tuner_priv;
+ int i, ret = 0;
+ unsigned char reg[] = {
+ 0x00, /* reg. 0x00: dummy */
+ 0x09, /* reg. 0x01 */
+ 0x16, /* reg. 0x02 */
+ 0x00, /* reg. 0x03 */
+ 0x00, /* reg. 0x04 */
+ 0x17, /* reg. 0x05 */
+ 0x02, /* reg. 0x06 */
+ 0x0a, /* reg. 0x07: CHECK */
+ 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256,
+ Loop Bw 1/8 */
+ 0x6f, /* reg. 0x09: enable LoopThrough */
+ 0xb8, /* reg. 0x0a: Disable LO Test Buffer */
+ 0x82, /* reg. 0x0b: CHECK */
+ 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */
+ 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */
+ 0x00, /* reg. 0x0e */
+ 0x00, /* reg. 0x0f */
+ 0x00, /* reg. 0x10 */
+ 0x00, /* reg. 0x11 */
+ 0x00, /* reg. 0x12 */
+ 0x00, /* reg. 0x13 */
+ 0x50, /* reg. 0x14: DVB-t High Gain, UHF.
+ Middle Gain: 0x48, Low Gain: 0x40 */
+ 0x01, /* reg. 0x15 */
+ };
+
+ switch (priv->xtal_freq) {
+ case FC_XTAL_27_MHZ:
+ case FC_XTAL_28_8_MHZ:
+ reg[0x07] |= 0x20;
+ break;
+ case FC_XTAL_36_MHZ:
+ default:
+ break;
+ }
+
+ if (priv->dual_master)
+ reg[0x0c] |= 0x02;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ for (i = 1; i < sizeof(reg); i++) {
+ ret = fc0013_writereg(priv, i, reg[i]);
+ if (ret)
+ break;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ if (ret)
+ err("fc0013_writereg failed: %d", ret);
+
+ return ret;
+}
+
+static int fc0013_sleep(struct dvb_frontend *fe)
+{
+ /* nothing to do here */
+ return 0;
+}
+
+int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val)
+{
+ struct fc0013_priv *priv = fe->tuner_priv;
+ int ret;
+ u8 rc_cal;
+ int val;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ /* push rc_cal value, get rc_cal value */
+ ret = fc0013_writereg(priv, 0x10, 0x00);
+ if (ret)
+ goto error_out;
+
+ /* get rc_cal value */
+ ret = fc0013_readreg(priv, 0x10, &rc_cal);
+ if (ret)
+ goto error_out;
+
+ rc_cal &= 0x0f;
+
+ val = (int)rc_cal + rc_val;
+
+ /* forcing rc_cal */
+ ret = fc0013_writereg(priv, 0x0d, 0x11);
+ if (ret)
+ goto error_out;
+
+ /* modify rc_cal value */
+ if (val > 15)
+ ret = fc0013_writereg(priv, 0x10, 0x0f);
+ else if (val < 0)
+ ret = fc0013_writereg(priv, 0x10, 0x00);
+ else
+ ret = fc0013_writereg(priv, 0x10, (u8)val);
+
+error_out:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ return ret;
+}
+EXPORT_SYMBOL(fc0013_rc_cal_add);
+
+int fc0013_rc_cal_reset(struct dvb_frontend *fe)
+{
+ struct fc0013_priv *priv = fe->tuner_priv;
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ ret = fc0013_writereg(priv, 0x0d, 0x01);
+ if (!ret)
+ ret = fc0013_writereg(priv, 0x10, 0x00);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ return ret;
+}
+EXPORT_SYMBOL(fc0013_rc_cal_reset);
+
+static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq)
+{
+ int ret;
+ u8 tmp;
+
+ ret = fc0013_readreg(priv, 0x1d, &tmp);
+ if (ret)
+ goto error_out;
+ tmp &= 0xe3;
+ if (freq <= 177500) { /* VHF Track: 7 */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);
+ } else if (freq <= 184500) { /* VHF Track: 6 */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x18);
+ } else if (freq <= 191500) { /* VHF Track: 5 */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x14);
+ } else if (freq <= 198500) { /* VHF Track: 4 */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x10);
+ } else if (freq <= 205500) { /* VHF Track: 3 */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c);
+ } else if (freq <= 219500) { /* VHF Track: 2 */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x08);
+ } else if (freq < 300000) { /* VHF Track: 1 */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x04);
+ } else { /* UHF and GPS */
+ ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c);
+ }
+ if (ret)
+ goto error_out;
+error_out:
+ return ret;
+}
+
+static int fc0013_set_params(struct dvb_frontend *fe)
+{
+ struct fc0013_priv *priv = fe->tuner_priv;
+ int i, ret = 0;
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ u32 freq = p->frequency / 1000;
+ u32 delsys = p->delivery_system;
+ unsigned char reg[7], am, pm, multi, tmp;
+ unsigned long f_vco;
+ unsigned short xtal_freq_khz_2, xin, xdiv;
+ int vco_select = false;
+
+ if (fe->callback) {
+ ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER,
+ FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1));
+ if (ret)
+ goto exit;
+ }
+
+ switch (priv->xtal_freq) {
+ case FC_XTAL_27_MHZ:
+ xtal_freq_khz_2 = 27000 / 2;
+ break;
+ case FC_XTAL_36_MHZ:
+ xtal_freq_khz_2 = 36000 / 2;
+ break;
+ case FC_XTAL_28_8_MHZ:
+ default:
+ xtal_freq_khz_2 = 28800 / 2;
+ break;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ /* set VHF track */
+ ret = fc0013_set_vhf_track(priv, freq);
+ if (ret)
+ goto exit;
+
+ if (freq < 300000) {
+ /* enable VHF filter */
+ ret = fc0013_readreg(priv, 0x07, &tmp);
+ if (ret)
+ goto exit;
+ ret = fc0013_writereg(priv, 0x07, tmp | 0x10);
+ if (ret)
+ goto exit;
+
+ /* disable UHF & disable GPS */
+ ret = fc0013_readreg(priv, 0x14, &tmp);
+ if (ret)
+ goto exit;
+ ret = fc0013_writereg(priv, 0x14, tmp & 0x1f);
+ if (ret)
+ goto exit;
+ } else if (freq <= 862000) {
+ /* disable VHF filter */
+ ret = fc0013_readreg(priv, 0x07, &tmp);
+ if (ret)
+ goto exit;
+ ret = fc0013_writereg(priv, 0x07, tmp & 0xef);
+ if (ret)
+ goto exit;
+
+ /* enable UHF & disable GPS */
+ ret = fc0013_readreg(priv, 0x14, &tmp);
+ if (ret)
+ goto exit;
+ ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40);
+ if (ret)
+ goto exit;
+ } else {
+ /* disable VHF filter */
+ ret = fc0013_readreg(priv, 0x07, &tmp);
+ if (ret)
+ goto exit;
+ ret = fc0013_writereg(priv, 0x07, tmp & 0xef);
+ if (ret)
+ goto exit;
+
+ /* disable UHF & enable GPS */
+ ret = fc0013_readreg(priv, 0x14, &tmp);
+ if (ret)
+ goto exit;
+ ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20);
+ if (ret)
+ goto exit;
+ }
+
+ /* select frequency divider and the frequency of VCO */
+ if (freq < 37084) { /* freq * 96 < 3560000 */
+ multi = 96;
+ reg[5] = 0x82;
+ reg[6] = 0x00;
+ } else if (freq < 55625) { /* freq * 64 < 3560000 */
+ multi = 64;
+ reg[5] = 0x02;
+ reg[6] = 0x02;
+ } else if (freq < 74167) { /* freq * 48 < 3560000 */
+ multi = 48;
+ reg[5] = 0x42;
+ reg[6] = 0x00;
+ } else if (freq < 111250) { /* freq * 32 < 3560000 */
+ multi = 32;
+ reg[5] = 0x82;
+ reg[6] = 0x02;
+ } else if (freq < 148334) { /* freq * 24 < 3560000 */
+ multi = 24;
+ reg[5] = 0x22;
+ reg[6] = 0x00;
+ } else if (freq < 222500) { /* freq * 16 < 3560000 */
+ multi = 16;
+ reg[5] = 0x42;
+ reg[6] = 0x02;
+ } else if (freq < 296667) { /* freq * 12 < 3560000 */
+ multi = 12;
+ reg[5] = 0x12;
+ reg[6] = 0x00;
+ } else if (freq < 445000) { /* freq * 8 < 3560000 */
+ multi = 8;
+ reg[5] = 0x22;
+ reg[6] = 0x02;
+ } else if (freq < 593334) { /* freq * 6 < 3560000 */
+ multi = 6;
+ reg[5] = 0x0a;
+ reg[6] = 0x00;
+ } else if (freq < 950000) { /* freq * 4 < 3800000 */
+ multi = 4;
+ reg[5] = 0x12;
+ reg[6] = 0x02;
+ } else {
+ multi = 2;
+ reg[5] = 0x0a;
+ reg[6] = 0x02;
+ }
+
+ f_vco = freq * multi;
+
+ if (f_vco >= 3060000) {
+ reg[6] |= 0x08;
+ vco_select = true;
+ }
+
+ if (freq >= 45000) {
+ /* From divided value (XDIV) determined the FA and FP value */
+ xdiv = (unsigned short)(f_vco / xtal_freq_khz_2);
+ if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2))
+ xdiv++;
+
+ pm = (unsigned char)(xdiv / 8);
+ am = (unsigned char)(xdiv - (8 * pm));
+
+ if (am < 2) {
+ reg[1] = am + 8;
+ reg[2] = pm - 1;
+ } else {
+ reg[1] = am;
+ reg[2] = pm;
+ }
+ } else {
+ /* fix for frequency less than 45 MHz */
+ reg[1] = 0x06;
+ reg[2] = 0x11;
+ }
+
+ /* fix clock out */
+ reg[6] |= 0x20;
+
+ /* From VCO frequency determines the XIN ( fractional part of Delta
+ Sigma PLL) and divided value (XDIV) */
+ xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2);
+ xin = (xin << 15) / xtal_freq_khz_2;
+ if (xin >= 16384)
+ xin += 32768;
+
+ reg[3] = xin >> 8;
+ reg[4] = xin & 0xff;
+
+ if (delsys == SYS_DVBT) {
+ reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */
+ switch (p->bandwidth_hz) {
+ case 6000000:
+ reg[6] |= 0x80;
+ break;
+ case 7000000:
+ reg[6] |= 0x40;
+ break;
+ case 8000000:
+ default:
+ break;
+ }
+ } else {
+ err("%s: modulation type not supported!", __func__);
+ return -EINVAL;
+ }
+
+ /* modified for Realtek demod */
+ reg[5] |= 0x07;
+
+ for (i = 1; i <= 6; i++) {
+ ret = fc0013_writereg(priv, i, reg[i]);
+ if (ret)
+ goto exit;
+ }
+
+ ret = fc0013_readreg(priv, 0x11, &tmp);
+ if (ret)
+ goto exit;
+ if (multi == 64)
+ ret = fc0013_writereg(priv, 0x11, tmp | 0x04);
+ else
+ ret = fc0013_writereg(priv, 0x11, tmp & 0xfb);
+ if (ret)
+ goto exit;
+
+ /* VCO Calibration */
+ ret = fc0013_writereg(priv, 0x0e, 0x80);
+ if (!ret)
+ ret = fc0013_writereg(priv, 0x0e, 0x00);
+
+ /* VCO Re-Calibration if needed */
+ if (!ret)
+ ret = fc0013_writereg(priv, 0x0e, 0x00);
+
+ if (!ret) {
+ msleep(10);
+ ret = fc0013_readreg(priv, 0x0e, &tmp);
+ }
+ if (ret)
+ goto exit;
+
+ /* vco selection */
+ tmp &= 0x3f;
+
+ if (vco_select) {
+ if (tmp > 0x3c) {
+ reg[6] &= ~0x08;
+ ret = fc0013_writereg(priv, 0x06, reg[6]);
+ if (!ret)
+ ret = fc0013_writereg(priv, 0x0e, 0x80);
+ if (!ret)
+ ret = fc0013_writereg(priv, 0x0e, 0x00);
+ }
+ } else {
+ if (tmp < 0x02) {
+ reg[6] |= 0x08;
+ ret = fc0013_writereg(priv, 0x06, reg[6]);
+ if (!ret)
+ ret = fc0013_writereg(priv, 0x0e, 0x80);
+ if (!ret)
+ ret = fc0013_writereg(priv, 0x0e, 0x00);
+ }
+ }
+
+ priv->frequency = p->frequency;
+ priv->bandwidth = p->bandwidth_hz;
+
+exit:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+ if (ret)
+ warn("%s: failed: %d", __func__, ret);
+ return ret;
+}
+
+static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct fc0013_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ /* always ? */
+ *frequency = 0;
+ return 0;
+}
+
+static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct fc0013_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+#define INPUT_ADC_LEVEL -8
+
+static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct fc0013_priv *priv = fe->tuner_priv;
+ int ret;
+ unsigned char tmp;
+ int int_temp, lna_gain, int_lna, tot_agc_gain, power;
+ const int fc0013_lna_gain_table[] = {
+ /* low gain */
+ -63, -58, -99, -73,
+ -63, -65, -54, -60,
+ /* middle gain */
+ 71, 70, 68, 67,
+ 65, 63, 61, 58,
+ /* high gain */
+ 197, 191, 188, 186,
+ 184, 182, 181, 179,
+ };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */
+
+ ret = fc0013_writereg(priv, 0x13, 0x00);
+ if (ret)
+ goto err;
+
+ ret = fc0013_readreg(priv, 0x13, &tmp);
+ if (ret)
+ goto err;
+ int_temp = tmp;
+
+ ret = fc0013_readreg(priv, 0x14, &tmp);
+ if (ret)
+ goto err;
+ lna_gain = tmp & 0x1f;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+
+ if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) {
+ int_lna = fc0013_lna_gain_table[lna_gain];
+ tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 +
+ (int_temp & 0x1f)) * 2;
+ power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10;
+
+ if (power >= 45)
+ *strength = 255; /* 100% */
+ else if (power < -95)
+ *strength = 0;
+ else
+ *strength = (power + 95) * 255 / 140;
+
+ *strength |= *strength << 8;
+ } else {
+ ret = -1;
+ }
+
+ goto exit;
+
+err:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */
+exit:
+ if (ret)
+ warn("%s: failed: %d", __func__, ret);
+ return ret;
+}
+
+static const struct dvb_tuner_ops fc0013_tuner_ops = {
+ .info = {
+ .name = "Fitipower FC0013",
+
+ .frequency_min = 37000000, /* estimate */
+ .frequency_max = 1680000000, /* CHECK */
+ .frequency_step = 0,
+ },
+
+ .release = fc0013_release,
+
+ .init = fc0013_init,
+ .sleep = fc0013_sleep,
+
+ .set_params = fc0013_set_params,
+
+ .get_frequency = fc0013_get_frequency,
+ .get_if_frequency = fc0013_get_if_frequency,
+ .get_bandwidth = fc0013_get_bandwidth,
+
+ .get_rf_strength = fc0013_get_rf_strength,
+};
+
+struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, u8 i2c_address, int dual_master,
+ enum fc001x_xtal_freq xtal_freq)
+{
+ struct fc0013_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c = i2c;
+ priv->dual_master = dual_master;
+ priv->addr = i2c_address;
+ priv->xtal_freq = xtal_freq;
+
+ info("Fitipower FC0013 successfully attached.");
+
+ fe->tuner_priv = priv;
+
+ memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ return fe;
+}
+EXPORT_SYMBOL(fc0013_attach);
+
+MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver");
+MODULE_AUTHOR("Hans-Frieder Vogt <hfvogt@gmx.net>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2");
diff --git a/drivers/media/common/tuners/fc0013.h b/drivers/media/common/tuners/fc0013.h
new file mode 100644
index 00000000000..594efd64aee
--- /dev/null
+++ b/drivers/media/common/tuners/fc0013.h
@@ -0,0 +1,57 @@
+/*
+ * Fitipower FC0013 tuner driver
+ *
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _FC0013_H_
+#define _FC0013_H_
+
+#include "dvb_frontend.h"
+#include "fc001x-common.h"
+
+#if defined(CONFIG_MEDIA_TUNER_FC0013) || \
+ (defined(CONFIG_MEDIA_TUNER_FC0013_MODULE) && defined(MODULE))
+extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ u8 i2c_address, int dual_master,
+ enum fc001x_xtal_freq xtal_freq);
+extern int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val);
+extern int fc0013_rc_cal_reset(struct dvb_frontend *fe);
+#else
+static inline struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c,
+ u8 i2c_address, int dual_master,
+ enum fc001x_xtal_freq xtal_freq)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val)
+{
+ return 0;
+}
+
+static inline int fc0013_rc_cal_reset(struct dvb_frontend *fe)
+{
+ return 0;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/fc001x-common.h b/drivers/media/common/tuners/fc001x-common.h
new file mode 100644
index 00000000000..71881815693
--- /dev/null
+++ b/drivers/media/common/tuners/fc001x-common.h
@@ -0,0 +1,39 @@
+/*
+ * Fitipower FC0012 & FC0013 tuner driver - common defines
+ *
+ * Copyright (C) 2012 Hans-Frieder Vogt <hfvogt@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 _FC001X_COMMON_H_
+#define _FC001X_COMMON_H_
+
+enum fc001x_xtal_freq {
+ FC_XTAL_27_MHZ, /* 27000000 */
+ FC_XTAL_28_8_MHZ, /* 28800000 */
+ FC_XTAL_36_MHZ, /* 36000000 */
+};
+
+/*
+ * enum fc001x_fe_callback_commands - Frontend callbacks
+ *
+ * @FC_FE_CALLBACK_VHF_ENABLE: enable VHF or UHF
+ */
+enum fc001x_fe_callback_commands {
+ FC_FE_CALLBACK_VHF_ENABLE,
+};
+
+#endif
diff --git a/drivers/media/common/tuners/tua9001.c b/drivers/media/common/tuners/tua9001.c
new file mode 100644
index 00000000000..de260708467
--- /dev/null
+++ b/drivers/media/common/tuners/tua9001.c
@@ -0,0 +1,215 @@
+/*
+ * Infineon TUA 9001 silicon tuner driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "tua9001.h"
+#include "tua9001_priv.h"
+
+/* write register */
+static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val)
+{
+ int ret;
+ u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff };
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg->i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n",
+ __func__, ret, reg);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+static int tua9001_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+
+ return 0;
+}
+
+static int tua9001_init(struct dvb_frontend *fe)
+{
+ struct tua9001_priv *priv = fe->tuner_priv;
+ int ret = 0;
+ u8 i;
+ struct reg_val data[] = {
+ { 0x1e, 0x6512 },
+ { 0x25, 0xb888 },
+ { 0x39, 0x5460 },
+ { 0x3b, 0x00c0 },
+ { 0x3a, 0xf000 },
+ { 0x08, 0x0000 },
+ { 0x32, 0x0030 },
+ { 0x41, 0x703a },
+ { 0x40, 0x1c78 },
+ { 0x2c, 0x1c00 },
+ { 0x36, 0xc013 },
+ { 0x37, 0x6f18 },
+ { 0x27, 0x0008 },
+ { 0x2a, 0x0001 },
+ { 0x34, 0x0a40 },
+ };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
+ if (ret)
+ break;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
+
+ if (ret < 0)
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tua9001_set_params(struct dvb_frontend *fe)
+{
+ struct tua9001_priv *priv = fe->tuner_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ u16 val;
+ u32 frequency;
+ struct reg_val data[2];
+
+ pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n",
+ __func__, c->delivery_system, c->frequency,
+ c->bandwidth_hz);
+
+ switch (c->delivery_system) {
+ case SYS_DVBT:
+ switch (c->bandwidth_hz) {
+ case 8000000:
+ val = 0x0000;
+ break;
+ case 7000000:
+ val = 0x1000;
+ break;
+ case 6000000:
+ val = 0x2000;
+ break;
+ case 5000000:
+ val = 0x3000;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ data[0].reg = 0x04;
+ data[0].val = val;
+
+ frequency = (c->frequency - 150000000);
+ frequency /= 100;
+ frequency *= 48;
+ frequency /= 10000;
+
+ data[1].reg = 0x1f;
+ data[1].val = frequency;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ ret = tua9001_wr_reg(priv, data[i].reg, data[i].val);
+ if (ret < 0)
+ break;
+ }
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */
+
+err:
+ if (ret < 0)
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ *frequency = 0; /* Zero-IF */
+
+ return 0;
+}
+
+static const struct dvb_tuner_ops tua9001_tuner_ops = {
+ .info = {
+ .name = "Infineon TUA 9001",
+
+ .frequency_min = 170000000,
+ .frequency_max = 862000000,
+ .frequency_step = 0,
+ },
+
+ .release = tua9001_release,
+
+ .init = tua9001_init,
+ .set_params = tua9001_set_params,
+
+ .get_if_frequency = tua9001_get_if_frequency,
+};
+
+struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tua9001_config *cfg)
+{
+ struct tua9001_priv *priv = NULL;
+
+ priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+
+ printk(KERN_INFO "Infineon TUA 9001 successfully attached.");
+
+ memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops,
+ sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+ return fe;
+}
+EXPORT_SYMBOL(tua9001_attach);
+
+MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/common/tuners/tua9001.h b/drivers/media/common/tuners/tua9001.h
new file mode 100644
index 00000000000..38d6ae76b1d
--- /dev/null
+++ b/drivers/media/common/tuners/tua9001.h
@@ -0,0 +1,46 @@
+/*
+ * Infineon TUA 9001 silicon tuner driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TUA9001_H
+#define TUA9001_H
+
+#include "dvb_frontend.h"
+
+struct tua9001_config {
+ /*
+ * I2C address
+ */
+ u8 i2c_addr;
+};
+
+#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \
+ (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tua9001_config *cfg);
+#else
+static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe,
+ struct i2c_adapter *i2c, struct tua9001_config *cfg)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif
diff --git a/drivers/media/common/tuners/tua9001_priv.h b/drivers/media/common/tuners/tua9001_priv.h
new file mode 100644
index 00000000000..73cc1ce0575
--- /dev/null
+++ b/drivers/media/common/tuners/tua9001_priv.h
@@ -0,0 +1,34 @@
+/*
+ * Infineon TUA 9001 silicon tuner driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef TUA9001_PRIV_H
+#define TUA9001_PRIV_H
+
+struct reg_val {
+ u8 reg;
+ u16 val;
+};
+
+struct tua9001_priv {
+ struct tua9001_config *cfg;
+ struct i2c_adapter *i2c;
+};
+
+#endif
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index eab2ea42420..dcca42ca57b 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -54,7 +54,7 @@ struct xc5000_priv {
struct list_head hybrid_tuner_instance_list;
u32 if_khz;
- u32 xtal_khz;
+ u16 xtal_khz;
u32 freq_hz;
u32 bandwidth;
u8 video_standard;
@@ -631,7 +631,10 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
ret = xc_load_i2c_sequence(fe, fw->data);
if (XC_RESULT_SUCCESS == ret)
ret = xc_set_xtal(fe);
- printk(KERN_INFO "xc5000: firmware upload complete...\n");
+ if (XC_RESULT_SUCCESS == ret)
+ printk(KERN_INFO "xc5000: firmware upload complete...\n");
+ else
+ printk(KERN_ERR "xc5000: firmware upload failed...\n");
}
out:
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
index 39a73bf0140..b1a54749462 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -34,7 +34,7 @@ struct xc5000_config {
u8 i2c_address;
u32 if_khz;
u8 radio_input;
- u32 xtal_khz;
+ u16 xtal_khz;
int chip_id;
};
diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c
index 48e48e8af55..66f52f116b6 100644
--- a/drivers/media/dvb/bt8xx/dst_ca.c
+++ b/drivers/media/dvb/bt8xx/dst_ca.c
@@ -477,7 +477,6 @@ static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message
static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg)
{
int i = 0;
- unsigned int ca_message_header_len;
u32 command = 0;
struct ca_msg *hw_buffer;
@@ -496,7 +495,6 @@ static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message,
if (p_ca_message->msg) {
- ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */
/* EN50221 tag */
command = 0;
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
index d88c4aa7d24..131b938e9e8 100644
--- a/drivers/media/dvb/ddbridge/ddbridge-core.c
+++ b/drivers/media/dvb/ddbridge/ddbridge-core.c
@@ -31,7 +31,6 @@
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/timer.h>
-#include <linux/version.h>
#include <linux/i2c.h>
#include <linux/swab.h>
#include <linux/vmalloc.h>
@@ -1696,7 +1695,7 @@ static struct pci_driver ddb_pci_driver = {
.name = "DDBridge",
.id_table = ddb_id_tbl,
.probe = ddb_probe,
- .remove = ddb_remove,
+ .remove = __devexit_p(ddb_remove),
};
static __init int module_init_ddbridge(void)
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c
index faa3671b649..d82469f842e 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb/dvb-core/dvb_demux.c
@@ -568,6 +568,16 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count)
}
EXPORT_SYMBOL(dvb_dmx_swfilter_204);
+void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count)
+{
+ spin_lock(&demux->lock);
+
+ demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK);
+
+ spin_unlock(&demux->lock);
+}
+EXPORT_SYMBOL(dvb_dmx_swfilter_raw);
+
static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux)
{
int i;
diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h
index a7d876fd02d..fa7188a253a 100644
--- a/drivers/media/dvb/dvb-core/dvb_demux.h
+++ b/drivers/media/dvb/dvb-core/dvb_demux.h
@@ -145,5 +145,7 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf,
void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count);
void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf,
size_t count);
+void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf,
+ size_t count);
#endif /* _DVB_DEMUX_H_ */
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index cb888d835a8..aebcdf221dd 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -182,13 +182,13 @@ static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system)
case SYS_DMBTH:
return DVBV3_OFDM;
case SYS_ATSC:
+ case SYS_ATSCMH:
case SYS_DVBC_ANNEX_B:
return DVBV3_ATSC;
case SYS_UNDEFINED:
case SYS_ISDBC:
case SYS_DVBH:
case SYS_DAB:
- case SYS_ATSCMH:
default:
/*
* Doesn't know how to emulate those types and/or
@@ -1030,6 +1030,25 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = {
_DTV_CMD(DTV_HIERARCHY, 0, 0),
_DTV_CMD(DTV_ENUM_DELSYS, 0, 0),
+
+ _DTV_CMD(DTV_ATSCMH_PARADE_ID, 1, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0),
+
+ _DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_PARADE_ID, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_NOG, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_TNOG, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SGN, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_PRC, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0),
+ _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0),
};
static void dtv_property_dump(struct dtv_property *tvp)
@@ -1121,6 +1140,8 @@ static int dtv_property_cache_sync(struct dvb_frontend *fe,
case DVBV3_ATSC:
dprintk("%s() Preparing ATSC req\n", __func__);
c->modulation = p->u.vsb.modulation;
+ if (c->delivery_system == SYS_ATSCMH)
+ break;
if ((c->modulation == VSB_8) || (c->modulation == VSB_16))
c->delivery_system = SYS_ATSC;
else
@@ -1367,6 +1388,54 @@ static int dtv_property_process_get(struct dvb_frontend *fe,
case DTV_DVBT2_PLP_ID:
tvp->u.data = c->dvbt2_plp_id;
break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_FIC_VER:
+ tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver;
+ break;
+ case DTV_ATSCMH_PARADE_ID:
+ tvp->u.data = fe->dtv_property_cache.atscmh_parade_id;
+ break;
+ case DTV_ATSCMH_NOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_nog;
+ break;
+ case DTV_ATSCMH_TNOG:
+ tvp->u.data = fe->dtv_property_cache.atscmh_tnog;
+ break;
+ case DTV_ATSCMH_SGN:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sgn;
+ break;
+ case DTV_ATSCMH_PRC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_prc;
+ break;
+ case DTV_ATSCMH_RS_FRAME_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_mode;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_ensemble;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_PRI:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_pri;
+ break;
+ case DTV_ATSCMH_RS_CODE_MODE_SEC:
+ tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_sec;
+ break;
+ case DTV_ATSCMH_SCCC_BLOCK_MODE:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_block_mode;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_A:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_a;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_B:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_b;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_C:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_c;
+ break;
+ case DTV_ATSCMH_SCCC_CODE_MODE_D:
+ tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d;
+ break;
+
default:
return -EINVAL;
}
@@ -1708,6 +1777,15 @@ static int dtv_property_process_set(struct dvb_frontend *fe,
case DTV_DVBT2_PLP_ID:
c->dvbt2_plp_id = tvp->u.data;
break;
+
+ /* ATSC-MH */
+ case DTV_ATSCMH_PARADE_ID:
+ fe->dtv_property_cache.atscmh_parade_id = tvp->u.data;
+ break;
+ case DTV_ATSCMH_RS_FRAME_ENSEMBLE:
+ fe->dtv_property_cache.atscmh_rs_frame_ensemble = tvp->u.data;
+ break;
+
default:
return -EINVAL;
}
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index d63a8215fe0..e929d5697b8 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -372,6 +372,24 @@ struct dtv_frontend_properties {
/* DVB-T2 specifics */
u32 dvbt2_plp_id;
+
+ /* ATSC-MH specifics */
+ u8 atscmh_fic_ver;
+ u8 atscmh_parade_id;
+ u8 atscmh_nog;
+ u8 atscmh_tnog;
+ u8 atscmh_sgn;
+ u8 atscmh_prc;
+
+ u8 atscmh_rs_frame_mode;
+ u8 atscmh_rs_frame_ensemble;
+ u8 atscmh_rs_code_mode_pri;
+ u8 atscmh_rs_code_mode_sec;
+ u8 atscmh_sccc_block_mode;
+ u8 atscmh_sccc_code_mode_a;
+ u8 atscmh_sccc_code_mode_b;
+ u8 atscmh_sccc_code_mode_c;
+ u8 atscmh_sccc_code_mode_d;
};
struct dvb_frontend {
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 63bf45679f9..a26949336b3 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -409,6 +409,7 @@ config DVB_USB_MXL111SF
tristate "MxL111SF DTV USB2.0 support"
depends on DVB_USB
select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
+ select DVB_LG2160 if !DVB_FE_CUSTOMISE
select VIDEO_TVEEPROM
help
Say Y here to support the MxL111SF USB2.0 DTV receiver.
@@ -422,3 +423,15 @@ config DVB_USB_RTL28XXU
select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
help
Say Y here to support the Realtek RTL28xxU DVB USB receiver.
+
+config DVB_USB_AF9035
+ tristate "Afatech AF9035 DVB-T USB2.0 support"
+ depends on DVB_USB
+ select DVB_AF9033
+ select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Afatech AF9035 based DVB USB receiver.
+
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index b76acb5387e..b667ac39a4e 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -110,6 +110,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
dvb-usb-rtl28xxu-objs = rtl28xxu.o
obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
+dvb-usb-af9035-objs = af9035.o
+obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o
+
ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/
# due to tuner-xc3028
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index 7e70ea50ef2..677fed79b01 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -244,8 +244,7 @@ static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
u8 uninitialized_var(mbox), addr_len;
struct req_t req;
-/* TODO: implement bus lock
-
+/*
The bus lock is needed because there is two tuners both using same I2C-address.
Due to that the only way to select correct tuner is use demodulator I2C-gate.
@@ -789,7 +788,7 @@ static void af9015_set_remote_config(struct usb_device *udev,
/* try to load remote based USB ID */
if (!props->rc.core.rc_codes)
props->rc.core.rc_codes = af9015_rc_setup_match(
- (vid << 16) + pid, af9015_rc_setup_usbids);
+ (vid << 16) | pid, af9015_rc_setup_usbids);
/* try to load remote based USB iManufacturer string */
if (!props->rc.core.rc_codes && vid == USB_VID_AFATECH) {
@@ -1220,8 +1219,8 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
}
/* attach demodulator */
- adap->fe_adap[0].fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id],
- &adap->dev->i2c_adap);
+ adap->fe_adap[0].fe = dvb_attach(af9013_attach,
+ &af9015_af9013_config[adap->id], &adap->dev->i2c_adap);
/*
* AF9015 firmware does not like if it gets interrupted by I2C adapter
@@ -1324,14 +1323,15 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
switch (af9015_af9013_config[adap->id].tuner) {
case AF9013_TUNER_MT2060:
case AF9013_TUNER_MT2060_2:
- ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
- &af9015_mt2060_config,
+ ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, &af9015_mt2060_config,
af9015_config.mt2060_if1[adap->id])
== NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_QT1010:
case AF9013_TUNER_QT1010A:
- ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap,
+ ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap,
&af9015_qt1010_config) == NULL ? -ENODEV : 0;
break;
case AF9013_TUNER_TDA18271:
@@ -1434,69 +1434,85 @@ enum af9015_usb_table_entry {
};
static struct usb_device_id af9015_usb_table[] = {
- [AFATECH_9015] =
- {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)},
- [AFATECH_9016] =
- {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)},
- [WINFAST_DTV_GOLD] =
- {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)},
- [PINNACLE_PCTV_71E] =
- {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)},
- [KWORLD_PLUSTV_399U] =
- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)},
- [TINYTWIN] = {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)},
- [AZUREWAVE_TU700] =
- {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)},
- [TERRATEC_AF9015] = {USB_DEVICE(USB_VID_TERRATEC,
+ [AFATECH_9015] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)},
+ [AFATECH_9016] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)},
+ [WINFAST_DTV_GOLD] = {
+ USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)},
+ [PINNACLE_PCTV_71E] = {
+ USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)},
+ [KWORLD_PLUSTV_399U] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)},
+ [TINYTWIN] = {
+ USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)},
+ [AZUREWAVE_TU700] = {
+ USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)},
+ [TERRATEC_AF9015] = {
+ USB_DEVICE(USB_VID_TERRATEC,
USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)},
- [KWORLD_PLUSTV_PC160] =
- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)},
- [AVERTV_VOLAR_X] =
- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
- [XTENSIONS_380U] =
- {USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
- [MSI_DIGIVOX_DUO] =
- {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)},
- [AVERTV_VOLAR_X_REV2] =
- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
- [TELESTAR_STARSTICK_2] =
- {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
- [AVERMEDIA_A309_USB] =
- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
- [MSI_DIGIVOX_MINI_III] =
- {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
- [KWORLD_E396] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)},
- [KWORLD_E39B] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)},
- [KWORLD_E395] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)},
- [TREKSTOR_DVBT] = {USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)},
- [AVERTV_A850] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)},
- [AVERTV_A805] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)},
- [CONCEPTRONIC_CTVDIGRCU] =
- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)},
- [KWORLD_MC810] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)},
- [GENIUS_TVGO_DVB_T03] =
- {USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)},
- [KWORLD_399U_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)},
- [KWORLD_PC160_T] =
- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)},
- [SVEON_STV20] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)},
- [TINYTWIN_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)},
- [WINFAST_DTV2000DS] =
- {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)},
- [KWORLD_UB383_T] =
- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)},
- [KWORLD_E39A] =
- {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)},
- [AVERMEDIA_A815M] =
- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)},
- [CINERGY_T_STICK_RC] = {USB_DEVICE(USB_VID_TERRATEC,
+ [KWORLD_PLUSTV_PC160] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)},
+ [AVERTV_VOLAR_X] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)},
+ [XTENSIONS_380U] = {
+ USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)},
+ [MSI_DIGIVOX_DUO] = {
+ USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)},
+ [AVERTV_VOLAR_X_REV2] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)},
+ [TELESTAR_STARSTICK_2] = {
+ USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)},
+ [AVERMEDIA_A309_USB] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
+ [MSI_DIGIVOX_MINI_III] = {
+ USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
+ [KWORLD_E396] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)},
+ [KWORLD_E39B] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)},
+ [KWORLD_E395] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)},
+ [TREKSTOR_DVBT] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)},
+ [AVERTV_A850] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)},
+ [AVERTV_A805] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)},
+ [CONCEPTRONIC_CTVDIGRCU] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)},
+ [KWORLD_MC810] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)},
+ [GENIUS_TVGO_DVB_T03] = {
+ USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)},
+ [KWORLD_399U_2] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)},
+ [KWORLD_PC160_T] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)},
+ [SVEON_STV20] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)},
+ [TINYTWIN_2] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)},
+ [WINFAST_DTV2000DS] = {
+ USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)},
+ [KWORLD_UB383_T] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)},
+ [KWORLD_E39A] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)},
+ [AVERMEDIA_A815M] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)},
+ [CINERGY_T_STICK_RC] = {
+ USB_DEVICE(USB_VID_TERRATEC,
USB_PID_TERRATEC_CINERGY_T_STICK_RC)},
- [CINERGY_T_DUAL_RC] = {USB_DEVICE(USB_VID_TERRATEC,
+ [CINERGY_T_DUAL_RC] = {
+ USB_DEVICE(USB_VID_TERRATEC,
USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)},
- [AVERTV_A850T] =
- {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)},
- [TINYTWIN_3] = {USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)},
- [SVEON_STV22] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)},
+ [AVERTV_A850T] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)},
+ [TINYTWIN_3] = {
+ USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)},
+ [SVEON_STV22] = {
+ USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)},
{ }
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1516,43 +1532,44 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_HAS_PID_FILTER |
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
-
- .pid_filter_count = 32,
- .pid_filter = af9015_pid_filter,
- .pid_filter_ctrl = af9015_pid_filter_ctrl,
-
- .frontend_attach =
- af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
+ .num_frontends = 1,
+ .fe = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .frontend_attach = af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ },
+ }
},
- }},
},
{
- .num_frontends = 1,
- .fe = {{
- .frontend_attach =
- af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x85,
- .u = {
- .bulk = {
- .buffersize =
- TS_USB20_FRAME_SIZE,
- }
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x85,
+ .u = {
+ .bulk = {
+ .buffersize = TS_USB20_FRAME_SIZE,
+ }
+ }
+ },
}
},
- }},
}
},
@@ -1575,102 +1592,67 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.cold_ids = {
&af9015_usb_table[AFATECH_9015],
&af9015_usb_table[AFATECH_9016],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Leadtek WinFast DTV Dongle Gold",
.cold_ids = {
&af9015_usb_table[WINFAST_DTV_GOLD],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Pinnacle PCTV 71e",
.cold_ids = {
&af9015_usb_table[PINNACLE_PCTV_71E],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "KWorld PlusTV Dual DVB-T Stick " \
"(DVB-T 399U)",
.cold_ids = {
&af9015_usb_table[KWORLD_PLUSTV_399U],
&af9015_usb_table[KWORLD_399U_2],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "DigitalNow TinyTwin DVB-T Receiver",
.cold_ids = {
&af9015_usb_table[TINYTWIN],
&af9015_usb_table[TINYTWIN_2],
&af9015_usb_table[TINYTWIN_3],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "TwinHan AzureWave AD-TU700(704J)",
.cold_ids = {
&af9015_usb_table[AZUREWAVE_TU700],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "TerraTec Cinergy T USB XE",
.cold_ids = {
&af9015_usb_table[TERRATEC_AF9015],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "KWorld PlusTV Dual DVB-T PCI " \
"(DVB-T PC160-2T)",
.cold_ids = {
&af9015_usb_table[KWORLD_PLUSTV_PC160],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "AVerMedia AVerTV DVB-T Volar X",
.cold_ids = {
&af9015_usb_table[AVERTV_VOLAR_X],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "TerraTec Cinergy T Stick RC",
.cold_ids = {
&af9015_usb_table[CINERGY_T_STICK_RC],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "TerraTec Cinergy T Stick Dual RC",
.cold_ids = {
&af9015_usb_table[CINERGY_T_DUAL_RC],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "AverMedia AVerTV Red HD+ (A850T)",
.cold_ids = {
&af9015_usb_table[AVERTV_A850T],
- NULL
},
- .warm_ids = {NULL},
},
}
}, {
@@ -1686,43 +1668,44 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_HAS_PID_FILTER |
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
-
- .pid_filter_count = 32,
- .pid_filter = af9015_pid_filter,
- .pid_filter_ctrl = af9015_pid_filter_ctrl,
-
- .frontend_attach =
- af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
+ .num_frontends = 1,
+ .fe = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .frontend_attach = af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ },
+ }
},
- }},
},
{
- .num_frontends = 1,
- .fe = {{
- .frontend_attach =
- af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x85,
- .u = {
- .bulk = {
- .buffersize =
- TS_USB20_FRAME_SIZE,
- }
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x85,
+ .u = {
+ .bulk = {
+ .buffersize = TS_USB20_FRAME_SIZE,
+ }
+ }
+ },
}
},
- }},
}
},
@@ -1744,51 +1727,33 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.name = "Xtensions XD-380",
.cold_ids = {
&af9015_usb_table[XTENSIONS_380U],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "MSI DIGIVOX Duo",
.cold_ids = {
&af9015_usb_table[MSI_DIGIVOX_DUO],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Fujitsu-Siemens Slim Mobile USB DVB-T",
.cold_ids = {
&af9015_usb_table[AVERTV_VOLAR_X_REV2],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Telestar Starstick 2",
.cold_ids = {
&af9015_usb_table[TELESTAR_STARSTICK_2],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "AVerMedia A309",
.cold_ids = {
&af9015_usb_table[AVERMEDIA_A309_USB],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "MSI Digi VOX mini III",
.cold_ids = {
&af9015_usb_table[MSI_DIGIVOX_MINI_III],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "KWorld USB DVB-T TV Stick II " \
"(VS-DVB-T 395U)",
.cold_ids = {
@@ -1796,34 +1761,23 @@ static struct dvb_usb_device_properties af9015_properties[] = {
&af9015_usb_table[KWORLD_E39B],
&af9015_usb_table[KWORLD_E395],
&af9015_usb_table[KWORLD_E39A],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "TrekStor DVB-T USB Stick",
.cold_ids = {
&af9015_usb_table[TREKSTOR_DVBT],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "AverMedia AVerTV Volar Black HD " \
"(A850)",
.cold_ids = {
&af9015_usb_table[AVERTV_A850],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Sveon STV22 Dual USB DVB-T Tuner HDTV",
.cold_ids = {
&af9015_usb_table[SVEON_STV22],
- NULL
},
- .warm_ids = {NULL},
},
}
}, {
@@ -1839,43 +1793,44 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.num_adapters = 2,
.adapter = {
{
- .num_frontends = 1,
- .fe = {{
- .caps = DVB_USB_ADAP_HAS_PID_FILTER |
- DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
-
- .pid_filter_count = 32,
- .pid_filter = af9015_pid_filter,
- .pid_filter_ctrl = af9015_pid_filter_ctrl,
-
- .frontend_attach =
- af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x84,
+ .num_frontends = 1,
+ .fe = {
+ {
+ .caps = DVB_USB_ADAP_HAS_PID_FILTER |
+ DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
+
+ .pid_filter_count = 32,
+ .pid_filter = af9015_pid_filter,
+ .pid_filter_ctrl = af9015_pid_filter_ctrl,
+
+ .frontend_attach = af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ },
+ }
},
- }},
},
{
- .num_frontends = 1,
- .fe = {{
- .frontend_attach =
- af9015_af9013_frontend_attach,
- .tuner_attach = af9015_tuner_attach,
- .stream = {
- .type = USB_BULK,
- .count = 6,
- .endpoint = 0x85,
- .u = {
- .bulk = {
- .buffersize =
- TS_USB20_FRAME_SIZE,
- }
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = af9015_af9013_frontend_attach,
+ .tuner_attach = af9015_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x85,
+ .u = {
+ .bulk = {
+ .buffersize = TS_USB20_FRAME_SIZE,
+ }
+ }
+ },
}
},
- }},
}
},
@@ -1897,76 +1852,50 @@ static struct dvb_usb_device_properties af9015_properties[] = {
.name = "AverMedia AVerTV Volar GPS 805 (A805)",
.cold_ids = {
&af9015_usb_table[AVERTV_A805],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Conceptronic USB2.0 DVB-T CTVDIGRCU " \
"V3.0",
.cold_ids = {
&af9015_usb_table[CONCEPTRONIC_CTVDIGRCU],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "KWorld Digial MC-810",
.cold_ids = {
&af9015_usb_table[KWORLD_MC810],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Genius TVGo DVB-T03",
.cold_ids = {
&af9015_usb_table[GENIUS_TVGO_DVB_T03],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "KWorld PlusTV DVB-T PCI Pro Card " \
"(DVB-T PC160-T)",
.cold_ids = {
&af9015_usb_table[KWORLD_PC160_T],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Sveon STV20 Tuner USB DVB-T HDTV",
.cold_ids = {
&af9015_usb_table[SVEON_STV20],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "Leadtek WinFast DTV2000DS",
.cold_ids = {
&af9015_usb_table[WINFAST_DTV2000DS],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "KWorld USB DVB-T Stick Mobile " \
"(UB383-T)",
.cold_ids = {
&af9015_usb_table[KWORLD_UB383_T],
- NULL
},
- .warm_ids = {NULL},
- },
- {
+ }, {
.name = "AverMedia AVerTV Volar M (A815Mac)",
.cold_ids = {
&af9015_usb_table[AVERMEDIA_A815M],
- NULL
},
- .warm_ids = {NULL},
},
}
},
@@ -2019,5 +1948,5 @@ static struct usb_driver af9015_usb_driver = {
module_usb_driver(af9015_usb_driver);
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T");
+MODULE_DESCRIPTION("Afatech AF9015 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c
new file mode 100644
index 00000000000..e83b39d3993
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/af9035.c
@@ -0,0 +1,1242 @@
+/*
+ * Afatech AF9035 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "af9035.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+static DEFINE_MUTEX(af9035_usb_mutex);
+static struct dvb_usb_device_properties af9035_properties[2];
+static int af9035_properties_count = ARRAY_SIZE(af9035_properties);
+
+static u16 af9035_checksum(const u8 *buf, size_t len)
+{
+ size_t i;
+ u16 checksum = 0;
+
+ for (i = 1; i < len; i++) {
+ if (i % 2)
+ checksum += buf[i] << 8;
+ else
+ checksum += buf[i];
+ }
+ checksum = ~checksum;
+
+ return checksum;
+}
+
+static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req)
+{
+#define BUF_LEN 64
+#define REQ_HDR_LEN 4 /* send header size */
+#define ACK_HDR_LEN 3 /* rece header size */
+#define CHECKSUM_LEN 2
+#define USB_TIMEOUT 2000
+
+ int ret, msg_len, act_len;
+ u8 buf[BUF_LEN];
+ static u8 seq; /* packet sequence number */
+ u16 checksum, tmp_checksum;
+
+ /* buffer overflow check */
+ if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) ||
+ req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) {
+ pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__,
+ req->wlen, req->rlen);
+ return -EINVAL;
+ }
+
+ if (mutex_lock_interruptible(&af9035_usb_mutex) < 0)
+ return -EAGAIN;
+
+ buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1;
+ buf[1] = req->mbox;
+ buf[2] = req->cmd;
+ buf[3] = seq++;
+ if (req->wlen)
+ memcpy(&buf[4], req->wbuf, req->wlen);
+
+ /* calc and add checksum */
+ checksum = af9035_checksum(buf, buf[0] - 1);
+ buf[buf[0] - 1] = (checksum >> 8);
+ buf[buf[0] - 0] = (checksum & 0xff);
+
+ msg_len = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN ;
+
+ /* send req */
+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len,
+ &act_len, USB_TIMEOUT);
+ if (ret < 0)
+ err("bulk message failed=%d (%d/%d)", ret, msg_len, act_len);
+ else
+ if (act_len != msg_len)
+ ret = -EIO; /* all data is not send */
+ if (ret < 0)
+ goto err_mutex_unlock;
+
+ /* no ack for those packets */
+ if (req->cmd == CMD_FW_DL)
+ goto exit_mutex_unlock;
+
+ /* receive ack and data if read req */
+ msg_len = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN;
+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len,
+ &act_len, USB_TIMEOUT);
+ if (ret < 0) {
+ err("recv bulk message failed=%d", ret);
+ ret = -EIO;
+ goto err_mutex_unlock;
+ }
+
+ if (act_len != msg_len) {
+ err("recv bulk message truncated (%d != %d)", act_len, msg_len);
+ ret = -EIO;
+ goto err_mutex_unlock;
+ }
+
+ /* verify checksum */
+ checksum = af9035_checksum(buf, act_len - 2);
+ tmp_checksum = (buf[act_len - 2] << 8) | buf[act_len - 1];
+ if (tmp_checksum != checksum) {
+ err("%s: command=%02x checksum mismatch (%04x != %04x)",
+ __func__, req->cmd, tmp_checksum, checksum);
+ ret = -EIO;
+ goto err_mutex_unlock;
+ }
+
+ /* check status */
+ if (buf[2]) {
+ pr_debug("%s: command=%02x failed fw error=%d\n", __func__,
+ req->cmd, buf[2]);
+ ret = -EIO;
+ goto err_mutex_unlock;
+ }
+
+ /* read request, copy returned data to return buf */
+ if (req->rlen)
+ memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen);
+
+err_mutex_unlock:
+exit_mutex_unlock:
+ mutex_unlock(&af9035_usb_mutex);
+
+ return ret;
+}
+
+/* write multiple registers */
+static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
+{
+ u8 wbuf[6 + len];
+ u8 mbox = (reg >> 16) & 0xff;
+ struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL };
+
+ wbuf[0] = len;
+ wbuf[1] = 2;
+ wbuf[2] = 0;
+ wbuf[3] = 0;
+ wbuf[4] = (reg >> 8) & 0xff;
+ wbuf[5] = (reg >> 0) & 0xff;
+ memcpy(&wbuf[6], val, len);
+
+ return af9035_ctrl_msg(d->udev, &req);
+}
+
+/* read multiple registers */
+static int af9035_rd_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len)
+{
+ u8 wbuf[] = { len, 2, 0, 0, (reg >> 8) & 0xff, reg & 0xff };
+ u8 mbox = (reg >> 16) & 0xff;
+ struct usb_req req = { CMD_MEM_RD, mbox, sizeof(wbuf), wbuf, len, val };
+
+ return af9035_ctrl_msg(d->udev, &req);
+}
+
+/* write single register */
+static int af9035_wr_reg(struct dvb_usb_device *d, u32 reg, u8 val)
+{
+ return af9035_wr_regs(d, reg, &val, 1);
+}
+
+/* read single register */
+static int af9035_rd_reg(struct dvb_usb_device *d, u32 reg, u8 *val)
+{
+ return af9035_rd_regs(d, reg, val, 1);
+}
+
+/* write single register with mask */
+static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val,
+ u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = af9035_rd_regs(d, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return af9035_wr_regs(d, reg, &val, 1);
+}
+
+static int af9035_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg msg[], int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct state *state = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ /*
+ * I2C sub header is 5 bytes long. Meaning of those bytes are:
+ * 0: data len
+ * 1: I2C addr << 1
+ * 2: reg addr len
+ * byte 3 and 4 can be used as reg addr
+ * 3: reg addr MSB
+ * used when reg addr len is set to 2
+ * 4: reg addr LSB
+ * used when reg addr len is set to 1 or 2
+ *
+ * For the simplify we do not use register addr at all.
+ * NOTE: As a firmware knows tuner type there is very small possibility
+ * there could be some tuner I2C hacks done by firmware and this may
+ * lead problems if firmware expects those bytes are used.
+ */
+ if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
+ (msg[1].flags & I2C_M_RD)) {
+ if (msg[0].len > 40 || msg[1].len > 40) {
+ /* TODO: correct limits > 40 */
+ ret = -EOPNOTSUPP;
+ } else if (msg[0].addr == state->af9033_config[0].i2c_addr) {
+ /* integrated demod */
+ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
+ msg[0].buf[2];
+ ret = af9035_rd_regs(d, reg, &msg[1].buf[0],
+ msg[1].len);
+ } else {
+ /* I2C */
+ u8 buf[5 + msg[0].len];
+ struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf),
+ buf, msg[1].len, msg[1].buf };
+ buf[0] = msg[1].len;
+ buf[1] = msg[0].addr << 1;
+ buf[2] = 0x00; /* reg addr len */
+ buf[3] = 0x00; /* reg addr MSB */
+ buf[4] = 0x00; /* reg addr LSB */
+ memcpy(&buf[5], msg[0].buf, msg[0].len);
+ ret = af9035_ctrl_msg(d->udev, &req);
+ }
+ } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ if (msg[0].len > 40) {
+ /* TODO: correct limits > 40 */
+ ret = -EOPNOTSUPP;
+ } else if (msg[0].addr == state->af9033_config[0].i2c_addr) {
+ /* integrated demod */
+ u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 |
+ msg[0].buf[2];
+ ret = af9035_wr_regs(d, reg, &msg[0].buf[3],
+ msg[0].len - 3);
+ } else {
+ /* I2C */
+ u8 buf[5 + msg[0].len];
+ struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf,
+ 0, NULL };
+ buf[0] = msg[0].len;
+ buf[1] = msg[0].addr << 1;
+ buf[2] = 0x00; /* reg addr len */
+ buf[3] = 0x00; /* reg addr MSB */
+ buf[4] = 0x00; /* reg addr LSB */
+ memcpy(&buf[5], msg[0].buf, msg[0].len);
+ ret = af9035_ctrl_msg(d->udev, &req);
+ }
+ } else {
+ /*
+ * We support only two kind of I2C transactions:
+ * 1) 1 x read + 1 x write
+ * 2) 1 x write
+ */
+ ret = -EOPNOTSUPP;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ if (ret < 0)
+ return ret;
+ else
+ return num;
+}
+
+static u32 af9035_i2c_functionality(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm af9035_i2c_algo = {
+ .master_xfer = af9035_i2c_master_xfer,
+ .functionality = af9035_i2c_functionality,
+};
+
+#define AF9035_POLL 250
+static int af9035_rc_query(struct dvb_usb_device *d)
+{
+ unsigned int key;
+ unsigned char b[4];
+ int ret;
+ struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, b };
+
+ ret = af9035_ctrl_msg(d->udev, &req);
+ if (ret < 0)
+ goto err;
+
+ if ((b[2] + b[3]) == 0xff) {
+ if ((b[0] + b[1]) == 0xff) {
+ /* NEC */
+ key = b[0] << 8 | b[2];
+ } else {
+ /* ext. NEC */
+ key = b[0] << 16 | b[1] << 8 | b[2];
+ }
+ } else {
+ key = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3];
+ }
+
+ rc_keydown(d->rc_dev, key, 0);
+
+err:
+ /* ignore errors */
+ return 0;
+}
+
+static int af9035_init(struct dvb_usb_device *d)
+{
+ struct state *state = d->priv;
+ int ret, i;
+ u16 frame_size = 87 * 188 / 4;
+ u8 packet_size = 512 / 4;
+ struct reg_val_mask tab[] = {
+ { 0x80f99d, 0x01, 0x01 },
+ { 0x80f9a4, 0x01, 0x01 },
+ { 0x00dd11, 0x00, 0x20 },
+ { 0x00dd11, 0x00, 0x40 },
+ { 0x00dd13, 0x00, 0x20 },
+ { 0x00dd13, 0x00, 0x40 },
+ { 0x00dd11, 0x20, 0x20 },
+ { 0x00dd88, (frame_size >> 0) & 0xff, 0xff},
+ { 0x00dd89, (frame_size >> 8) & 0xff, 0xff},
+ { 0x00dd0c, packet_size, 0xff},
+ { 0x00dd11, state->dual_mode << 6, 0x40 },
+ { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff},
+ { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff},
+ { 0x00dd0d, packet_size, 0xff },
+ { 0x80f9a3, 0x00, 0x01 },
+ { 0x80f9cd, 0x00, 0x01 },
+ { 0x80f99d, 0x00, 0x01 },
+ { 0x80f9a4, 0x00, 0x01 },
+ };
+
+ pr_debug("%s: USB speed=%d frame_size=%04x packet_size=%02x\n",
+ __func__, d->udev->speed, frame_size, packet_size);
+
+ /* init endpoints */
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = af9035_wr_reg_mask(d, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int ret;
+ u8 wbuf[1] = { 1 };
+ u8 rbuf[4];
+ struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf,
+ sizeof(rbuf), rbuf };
+
+ ret = af9035_ctrl_msg(udev, &req);
+ if (ret < 0)
+ goto err;
+
+ pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__,
+ rbuf[0], rbuf[1], rbuf[2], rbuf[3]);
+ if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])
+ *cold = 0;
+ else
+ *cold = 1;
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_download_firmware(struct usb_device *udev,
+ const struct firmware *fw)
+{
+ int ret, i, j, len;
+ u8 wbuf[1];
+ u8 rbuf[4];
+ struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
+ struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL };
+ struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
+ u8 hdr_core;
+ u16 hdr_addr, hdr_data_len, hdr_checksum;
+ #define MAX_DATA 58
+ #define HDR_SIZE 7
+
+ /*
+ * Thanks to Daniel Glöckner <daniel-gl@gmx.net> about that info!
+ *
+ * byte 0: MCS 51 core
+ * There are two inside the AF9035 (1=Link and 2=OFDM) with separate
+ * address spaces
+ * byte 1-2: Big endian destination address
+ * byte 3-4: Big endian number of data bytes following the header
+ * byte 5-6: Big endian header checksum, apparently ignored by the chip
+ * Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256)
+ */
+
+ for (i = fw->size; i > HDR_SIZE;) {
+ hdr_core = fw->data[fw->size - i + 0];
+ hdr_addr = fw->data[fw->size - i + 1] << 8;
+ hdr_addr |= fw->data[fw->size - i + 2] << 0;
+ hdr_data_len = fw->data[fw->size - i + 3] << 8;
+ hdr_data_len |= fw->data[fw->size - i + 4] << 0;
+ hdr_checksum = fw->data[fw->size - i + 5] << 8;
+ hdr_checksum |= fw->data[fw->size - i + 6] << 0;
+
+ pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n",
+ __func__, hdr_core, hdr_addr, hdr_data_len,
+ hdr_checksum);
+
+ if (((hdr_core != 1) && (hdr_core != 2)) ||
+ (hdr_data_len > i)) {
+ pr_debug("%s: bad firmware\n", __func__);
+ break;
+ }
+
+ /* download begin packet */
+ req.cmd = CMD_FW_DL_BEGIN;
+ ret = af9035_ctrl_msg(udev, &req);
+ if (ret < 0)
+ goto err;
+
+ /* download firmware packet(s) */
+ for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) {
+ len = j;
+ if (len > MAX_DATA)
+ len = MAX_DATA;
+ req_fw_dl.wlen = len;
+ req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i +
+ HDR_SIZE + hdr_data_len - j];
+ ret = af9035_ctrl_msg(udev, &req_fw_dl);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* download end packet */
+ req.cmd = CMD_FW_DL_END;
+ ret = af9035_ctrl_msg(udev, &req);
+ if (ret < 0)
+ goto err;
+
+ i -= hdr_data_len + HDR_SIZE;
+
+ pr_debug("%s: data uploaded=%zu\n", __func__, fw->size - i);
+ }
+
+ /* firmware loaded, request boot */
+ req.cmd = CMD_FW_BOOT;
+ ret = af9035_ctrl_msg(udev, &req);
+ if (ret < 0)
+ goto err;
+
+ /* ensure firmware starts */
+ wbuf[0] = 1;
+ ret = af9035_ctrl_msg(udev, &req_fw_ver);
+ if (ret < 0)
+ goto err;
+
+ if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
+ info("firmware did not run");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2],
+ rbuf[3]);
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_download_firmware_it9135(struct usb_device *udev,
+ const struct firmware *fw)
+{
+ int ret, i, i_prev;
+ u8 wbuf[1];
+ u8 rbuf[4];
+ struct usb_req req = { 0, 0, 0, NULL, 0, NULL };
+ struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL };
+ struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ;
+ #define HDR_SIZE 7
+
+ /*
+ * There seems to be following firmware header. Meaning of bytes 0-3
+ * is unknown.
+ *
+ * 0: 3
+ * 1: 0, 1
+ * 2: 0
+ * 3: 1, 2, 3
+ * 4: addr MSB
+ * 5: addr LSB
+ * 6: count of data bytes ?
+ */
+
+ for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) {
+ if (i == fw->size ||
+ (fw->data[i + 0] == 0x03 &&
+ (fw->data[i + 1] == 0x00 ||
+ fw->data[i + 1] == 0x01) &&
+ fw->data[i + 2] == 0x00)) {
+ req_fw_dl.wlen = i - i_prev;
+ req_fw_dl.wbuf = (u8 *) &fw->data[i_prev];
+ i_prev = i;
+ ret = af9035_ctrl_msg(udev, &req_fw_dl);
+ if (ret < 0)
+ goto err;
+
+ pr_debug("%s: data uploaded=%d\n", __func__, i);
+ }
+ }
+
+ /* firmware loaded, request boot */
+ req.cmd = CMD_FW_BOOT;
+ ret = af9035_ctrl_msg(udev, &req);
+ if (ret < 0)
+ goto err;
+
+ /* ensure firmware starts */
+ wbuf[0] = 1;
+ ret = af9035_ctrl_msg(udev, &req_fw_ver);
+ if (ret < 0)
+ goto err;
+
+ if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) {
+ info("firmware did not run");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2],
+ rbuf[3]);
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+/* abuse that callback as there is no better one for reading eeprom */
+static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ struct state *state = d->priv;
+ int ret, i, eeprom_shift = 0;
+ u8 tmp;
+ u16 tmp16;
+
+ /* check if there is dual tuners */
+ ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp);
+ if (ret < 0)
+ goto err;
+
+ state->dual_mode = tmp;
+ pr_debug("%s: dual mode=%d\n", __func__, state->dual_mode);
+
+ for (i = 0; i < af9035_properties[0].num_adapters; i++) {
+ /* tuner */
+ ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp);
+ if (ret < 0)
+ goto err;
+
+ state->af9033_config[i].tuner = tmp;
+ pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp);
+
+ switch (tmp) {
+ case AF9033_TUNER_TUA9001:
+ case AF9033_TUNER_FC0011:
+ case AF9033_TUNER_MXL5007T:
+ case AF9033_TUNER_TDA18218:
+ state->af9033_config[i].spec_inv = 1;
+ break;
+ default:
+ warn("tuner ID=%02x not supported, please report!",
+ tmp);
+ };
+
+ /* tuner IF frequency */
+ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp16 = tmp;
+
+ ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp16 |= tmp << 8;
+
+ pr_debug("%s: [%d]IF=%d\n", __func__, i, tmp16);
+
+ eeprom_shift = 0x10; /* shift for the 2nd tuner params */
+ }
+
+ /* get demod clock */
+ ret = af9035_rd_reg(d, 0x00d800, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp = (tmp >> 0) & 0x0f;
+
+ for (i = 0; i < af9035_properties[0].num_adapters; i++)
+ state->af9033_config[i].clock = clock_lut[tmp];
+
+ ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp);
+ if (ret < 0)
+ goto err;
+ pr_debug("%s: ir_mode=%02x\n", __func__, tmp);
+
+ /* don't activate rc if in HID mode or if not available */
+ if (tmp == 5) {
+ ret = af9035_rd_reg(d, EEPROM_IR_TYPE, &tmp);
+ if (ret < 0)
+ goto err;
+ pr_debug("%s: ir_type=%02x\n", __func__, tmp);
+
+ switch (tmp) {
+ case 0: /* NEC */
+ default:
+ d->props.rc.core.protocol = RC_TYPE_NEC;
+ d->props.rc.core.allowed_protos = RC_TYPE_NEC;
+ break;
+ case 1: /* RC6 */
+ d->props.rc.core.protocol = RC_TYPE_RC6;
+ d->props.rc.core.allowed_protos = RC_TYPE_RC6;
+ break;
+ }
+ d->props.rc.core.rc_query = af9035_rc_query;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+/* abuse that callback as there is no better one for reading eeprom */
+static int af9035_read_mac_address_it9135(struct dvb_usb_device *d, u8 mac[6])
+{
+ struct state *state = d->priv;
+ int ret, i;
+ u8 tmp;
+
+ state->dual_mode = false;
+
+ /* get demod clock */
+ ret = af9035_rd_reg(d, 0x00d800, &tmp);
+ if (ret < 0)
+ goto err;
+
+ tmp = (tmp >> 0) & 0x0f;
+
+ for (i = 0; i < af9035_properties[0].num_adapters; i++)
+ state->af9033_config[i].clock = clock_lut_it9135[tmp];
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d,
+ int cmd, int arg)
+{
+ int ret;
+
+ switch (cmd) {
+ case FC0011_FE_CALLBACK_POWER:
+ /* Tuner enable */
+ ret = af9035_wr_reg_mask(d, 0xd8eb, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8ec, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8ed, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ /* LED */
+ ret = af9035_wr_reg_mask(d, 0xd8d0, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(d, 0xd8d1, 1, 1);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(10000, 50000);
+ break;
+ case FC0011_FE_CALLBACK_RESET:
+ ret = af9035_wr_reg(d, 0xd8e9, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg(d, 0xd8e8, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg(d, 0xd8e7, 1);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(10000, 20000);
+
+ ret = af9035_wr_reg(d, 0xd8e7, 0);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(10000, 20000);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg)
+{
+ struct state *state = d->priv;
+
+ switch (state->af9033_config[0].tuner) {
+ case AF9033_TUNER_FC0011:
+ return af9035_fc0011_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return -ENODEV;
+}
+
+static int af9035_frontend_callback(void *adapter_priv, int component,
+ int cmd, int arg)
+{
+ struct i2c_adapter *adap = adapter_priv;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+
+ switch (component) {
+ case DVB_FRONTEND_COMPONENT_TUNER:
+ return af9035_tuner_callback(d, cmd, arg);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int af9035_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct state *state = adap->dev->priv;
+ int ret;
+
+ if (!state->af9033_config[adap->id].tuner) {
+ /* unsupported tuner */
+ ret = -ENODEV;
+ goto err;
+ }
+
+ if (adap->id == 0) {
+ state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB;
+ state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL;
+
+ ret = af9035_wr_reg(adap->dev, 0x00417f,
+ state->af9033_config[1].i2c_addr);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg(adap->dev, 0x00d81a,
+ state->dual_mode);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* attach demodulator */
+ adap->fe_adap[0].fe = dvb_attach(af9033_attach,
+ &state->af9033_config[adap->id], &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ /* disable I2C-gate */
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL;
+ adap->fe_adap[0].fe->callback = af9035_frontend_callback;
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static struct tua9001_config af9035_tua9001_config = {
+ .i2c_addr = 0x60,
+};
+
+static const struct fc0011_config af9035_fc0011_config = {
+ .i2c_address = 0x60,
+};
+
+static struct mxl5007t_config af9035_mxl5007t_config = {
+ .xtal_freq_hz = MxL_XTAL_24_MHZ,
+ .if_freq_hz = MxL_IF_4_57_MHZ,
+ .invert_if = 0,
+ .loop_thru_enable = 0,
+ .clk_out_enable = 0,
+ .clk_out_amp = MxL_CLKOUT_AMP_0_94V,
+};
+
+static struct tda18218_config af9035_tda18218_config = {
+ .i2c_address = 0x60,
+ .i2c_wr_max = 21,
+};
+
+static int af9035_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct state *state = adap->dev->priv;
+ int ret;
+ struct dvb_frontend *fe;
+
+ switch (state->af9033_config[adap->id].tuner) {
+ case AF9033_TUNER_TUA9001:
+ /* AF9035 gpiot3 = TUA9001 RESETN
+ AF9035 gpiot2 = TUA9001 RXEN */
+
+ /* configure gpiot2 and gpiot2 as output */
+ ret = af9035_wr_reg_mask(adap->dev, 0x00d8ec, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(adap->dev, 0x00d8ed, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(adap->dev, 0x00d8e8, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9035_wr_reg_mask(adap->dev, 0x00d8e9, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ /* reset tuner */
+ ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(2000, 20000);
+
+ ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ /* activate tuner RX */
+ /* TODO: use callback for TUA9001 RXEN */
+ ret = af9035_wr_reg_mask(adap->dev, 0x00d8eb, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+
+ /* attach tuner */
+ fe = dvb_attach(tua9001_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, &af9035_tua9001_config);
+ break;
+ case AF9033_TUNER_FC0011:
+ fe = dvb_attach(fc0011_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, &af9035_fc0011_config);
+ break;
+ case AF9033_TUNER_MXL5007T:
+ ret = af9035_wr_reg(adap->dev, 0x00d8e0, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(adap->dev, 0x00d8e1, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(adap->dev, 0x00d8df, 0);
+ if (ret < 0)
+ goto err;
+
+ msleep(30);
+
+ ret = af9035_wr_reg(adap->dev, 0x00d8df, 1);
+ if (ret < 0)
+ goto err;
+
+ msleep(300);
+
+ ret = af9035_wr_reg(adap->dev, 0x00d8c0, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(adap->dev, 0x00d8c1, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(adap->dev, 0x00d8bf, 0);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(adap->dev, 0x00d8b4, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(adap->dev, 0x00d8b5, 1);
+ if (ret < 0)
+ goto err;
+ ret = af9035_wr_reg(adap->dev, 0x00d8b3, 1);
+ if (ret < 0)
+ goto err;
+
+ /* attach tuner */
+ fe = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, 0x60, &af9035_mxl5007t_config);
+ break;
+ case AF9033_TUNER_TDA18218:
+ /* attach tuner */
+ fe = dvb_attach(tda18218_attach, adap->fe_adap[0].fe,
+ &adap->dev->i2c_adap, &af9035_tda18218_config);
+ break;
+ default:
+ fe = NULL;
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+enum af9035_id_entry {
+ AF9035_15A4_9035,
+ AF9035_15A4_1000,
+ AF9035_15A4_1001,
+ AF9035_15A4_1002,
+ AF9035_15A4_1003,
+ AF9035_0CCD_0093,
+ AF9035_07CA_A835,
+ AF9035_07CA_B835,
+ AF9035_07CA_1867,
+ AF9035_07CA_A867,
+ AF9035_07CA_0825,
+};
+
+static struct usb_device_id af9035_id[] = {
+ [AF9035_15A4_9035] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035)},
+ [AF9035_15A4_1000] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1000)},
+ [AF9035_15A4_1001] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1001)},
+ [AF9035_15A4_1002] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1002)},
+ [AF9035_15A4_1003] = {
+ USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1003)},
+ [AF9035_0CCD_0093] = {
+ USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)},
+ [AF9035_07CA_A835] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835)},
+ [AF9035_07CA_B835] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835)},
+ [AF9035_07CA_1867] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)},
+ [AF9035_07CA_A867] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867)},
+ [AF9035_07CA_0825] = {
+ USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR)},
+ {},
+};
+
+MODULE_DEVICE_TABLE(usb, af9035_id);
+
+static struct dvb_usb_device_properties af9035_properties[] = {
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = af9035_download_firmware,
+ .firmware = "dvb-usb-af9035-02.fw",
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = af9035_frontend_attach,
+ .tuner_attach = af9035_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ .u = {
+ .bulk = {
+ .buffersize = (87 * 188),
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .identify_state = af9035_identify_state,
+ .read_mac_address = af9035_read_mac_address,
+
+ .i2c_algo = &af9035_i2c_algo,
+
+ .rc.core = {
+ .protocol = RC_TYPE_UNKNOWN,
+ .module_name = "af9035",
+ .rc_query = NULL,
+ .rc_interval = AF9035_POLL,
+ .allowed_protos = RC_TYPE_UNKNOWN,
+ .rc_codes = RC_MAP_EMPTY,
+ },
+ .num_device_descs = 5,
+ .devices = {
+ {
+ .name = "Afatech AF9035 reference design",
+ .cold_ids = {
+ &af9035_id[AF9035_15A4_9035],
+ &af9035_id[AF9035_15A4_1000],
+ &af9035_id[AF9035_15A4_1001],
+ &af9035_id[AF9035_15A4_1002],
+ &af9035_id[AF9035_15A4_1003],
+ },
+ }, {
+ .name = "TerraTec Cinergy T Stick",
+ .cold_ids = {
+ &af9035_id[AF9035_0CCD_0093],
+ },
+ }, {
+ .name = "AVerMedia AVerTV Volar HD/PRO (A835)",
+ .cold_ids = {
+ &af9035_id[AF9035_07CA_A835],
+ &af9035_id[AF9035_07CA_B835],
+ },
+ }, {
+ .name = "AVerMedia HD Volar (A867)",
+ .cold_ids = {
+ &af9035_id[AF9035_07CA_1867],
+ &af9035_id[AF9035_07CA_A867],
+ },
+ }, {
+ .name = "AVerMedia Twinstar (A825)",
+ .cold_ids = {
+ &af9035_id[AF9035_07CA_0825],
+ },
+ },
+ }
+ },
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .download_firmware = af9035_download_firmware_it9135,
+ .firmware = "dvb-usb-it9135-01.fw",
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = af9035_frontend_attach,
+ .tuner_attach = af9035_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x84,
+ .u = {
+ .bulk = {
+ .buffersize = (87 * 188),
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .identify_state = af9035_identify_state,
+ .read_mac_address = af9035_read_mac_address_it9135,
+
+ .i2c_algo = &af9035_i2c_algo,
+
+ .num_device_descs = 0, /* disabled as no support for IT9135 */
+ .devices = {
+ {
+ .name = "ITE Tech. IT9135 reference design",
+ },
+ }
+ },
+};
+
+static int af9035_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret, i;
+ struct dvb_usb_device *d = NULL;
+ struct usb_device *udev;
+ bool found;
+
+ pr_debug("%s: interface=%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ /* interface 0 is used by DVB-T receiver and
+ interface 1 is for remote controller (HID) */
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return 0;
+
+ /* Dynamic USB ID support. Replaces first device ID with current one. */
+ udev = interface_to_usbdev(intf);
+
+ for (i = 0, found = false; i < ARRAY_SIZE(af9035_id) - 1; i++) {
+ if (af9035_id[i].idVendor ==
+ le16_to_cpu(udev->descriptor.idVendor) &&
+ af9035_id[i].idProduct ==
+ le16_to_cpu(udev->descriptor.idProduct)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ pr_debug("%s: using dynamic ID %04x:%04x\n", __func__,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+ af9035_properties[0].devices[0].cold_ids[0]->idVendor =
+ le16_to_cpu(udev->descriptor.idVendor);
+ af9035_properties[0].devices[0].cold_ids[0]->idProduct =
+ le16_to_cpu(udev->descriptor.idProduct);
+ }
+
+
+ for (i = 0; i < af9035_properties_count; i++) {
+ ret = dvb_usb_device_init(intf, &af9035_properties[i],
+ THIS_MODULE, &d, adapter_nr);
+
+ if (ret == -ENODEV)
+ continue;
+ else
+ break;
+ }
+
+ if (ret < 0)
+ goto err;
+
+ if (d) {
+ ret = af9035_init(d);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver af9035_usb_driver = {
+ .name = "dvb_usb_af9035",
+ .probe = af9035_usb_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = af9035_id,
+};
+
+module_usb_driver(af9035_usb_driver);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9035 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h
new file mode 100644
index 00000000000..481a1a43dd2
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/af9035.h
@@ -0,0 +1,113 @@
+/*
+ * Afatech AF9035 DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef AF9035_H
+#define AF9035_H
+
+/* prefix for dvb-usb log writings */
+#define DVB_USB_LOG_PREFIX "af9035"
+
+#include "dvb-usb.h"
+#include "af9033.h"
+#include "tua9001.h"
+#include "fc0011.h"
+#include "mxl5007t.h"
+#include "tda18218.h"
+
+struct reg_val {
+ u32 reg;
+ u8 val;
+};
+
+struct reg_val_mask {
+ u32 reg;
+ u8 val;
+ u8 mask;
+};
+
+struct usb_req {
+ u8 cmd;
+ u8 mbox;
+ u8 wlen;
+ u8 *wbuf;
+ u8 rlen;
+ u8 *rbuf;
+};
+
+struct state {
+ bool dual_mode;
+
+ struct af9033_config af9033_config[2];
+};
+
+u32 clock_lut[] = {
+ 20480000, /* FPGA */
+ 16384000, /* 16.38 MHz */
+ 20480000, /* 20.48 MHz */
+ 36000000, /* 36.00 MHz */
+ 30000000, /* 30.00 MHz */
+ 26000000, /* 26.00 MHz */
+ 28000000, /* 28.00 MHz */
+ 32000000, /* 32.00 MHz */
+ 34000000, /* 34.00 MHz */
+ 24000000, /* 24.00 MHz */
+ 22000000, /* 22.00 MHz */
+ 12000000, /* 12.00 MHz */
+};
+
+u32 clock_lut_it9135[] = {
+ 12000000, /* 12.00 MHz */
+ 20480000, /* 20.48 MHz */
+ 36000000, /* 36.00 MHz */
+ 30000000, /* 30.00 MHz */
+ 26000000, /* 26.00 MHz */
+ 28000000, /* 28.00 MHz */
+ 32000000, /* 32.00 MHz */
+ 34000000, /* 34.00 MHz */
+ 24000000, /* 24.00 MHz */
+ 22000000, /* 22.00 MHz */
+};
+
+/* EEPROM locations */
+#define EEPROM_IR_MODE 0x430d
+#define EEPROM_DUAL_MODE 0x4326
+#define EEPROM_IR_TYPE 0x4329
+#define EEPROM_1_IFFREQ_L 0x432d
+#define EEPROM_1_IFFREQ_H 0x432e
+#define EEPROM_1_TUNER_ID 0x4331
+#define EEPROM_2_IFFREQ_L 0x433d
+#define EEPROM_2_IFFREQ_H 0x433e
+#define EEPROM_2_TUNER_ID 0x4341
+
+/* USB commands */
+#define CMD_MEM_RD 0x00
+#define CMD_MEM_WR 0x01
+#define CMD_I2C_RD 0x02
+#define CMD_I2C_WR 0x03
+#define CMD_IR_GET 0x18
+#define CMD_FW_DL 0x21
+#define CMD_FW_QUERYINFO 0x22
+#define CMD_FW_BOOT 0x23
+#define CMD_FW_DL_BEGIN 0x24
+#define CMD_FW_DL_END 0x25
+#define CMD_FW_SCATTER_WR 0x29
+
+#endif
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 02290c60f72..7e9e00fae04 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -32,7 +32,7 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion,
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ return -EINTR;
}
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0),
@@ -118,7 +118,7 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ return -EINTR;
}
st->buf[0] = REQUEST_SET_GPIO;
@@ -139,7 +139,7 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets)
if (st->fw_version >= 0x10201) {
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ return -EINTR;
}
st->buf[0] = REQUEST_SET_USB_XFER_LEN;
@@ -178,7 +178,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
/* Ensure nobody else hits the i2c bus while we're sending our
sequence of messages, (such as the remote control thread) */
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+ return -EINTR;
for (i = 0; i < num; i++) {
if (i == 0) {
@@ -228,7 +228,8 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
/* Write request */
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ mutex_unlock(&d->i2c_mutex);
+ return -EINTR;
}
st->buf[0] = REQUEST_NEW_I2C_WRITE;
st->buf[1] = msg[i].addr << 1;
@@ -271,10 +272,11 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
int i,len;
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+ return -EINTR;
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ mutex_unlock(&d->i2c_mutex);
+ return -EINTR;
}
for (i = 0; i < num; i++) {
@@ -369,7 +371,7 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll,
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ return -EINTR;
}
st->buf[0] = REQUEST_SET_CLOCK;
@@ -401,7 +403,7 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz)
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ return -EINTR;
}
st->buf[0] = REQUEST_SET_I2C_PARAM;
@@ -561,7 +563,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ return -EINTR;
}
st->buf[0] = REQUEST_ENABLE_VIDEO;
@@ -611,7 +613,7 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type)
if (mutex_lock_interruptible(&d->usb_mutex) < 0) {
err("could not acquire lock");
- return 0;
+ return -EINTR;
}
st->buf[0] = REQUEST_SET_RC;
diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
index f9e966aa26e..510001da6e8 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
@@ -3569,6 +3569,7 @@ struct usb_device_id dib0700_usb_id_table[] = {
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090E) },
{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790E) },
/* 80 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) },
+ { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) },
{ 0 } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
@@ -3832,7 +3833,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
},
},
- .num_device_descs = 11,
+ .num_device_descs = 12,
.devices = {
{ "DiBcom STK7070P reference design",
{ &dib0700_usb_id_table[15], NULL },
@@ -3878,6 +3879,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
{ &dib0700_usb_id_table[50], NULL },
{ NULL },
},
+ { "Elgato EyeTV DTT rev. 2",
+ { &dib0700_usb_id_table[81], NULL },
+ { NULL },
+ },
},
.rc.core = {
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 397d8f23273..7a6160bf54b 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -76,6 +76,11 @@
#define USB_PID_AFATECH_AF9005 0x9020
#define USB_PID_AFATECH_AF9015_9015 0x9015
#define USB_PID_AFATECH_AF9015_9016 0x9016
+#define USB_PID_AFATECH_AF9035_1000 0x1000
+#define USB_PID_AFATECH_AF9035_1001 0x1001
+#define USB_PID_AFATECH_AF9035_1002 0x1002
+#define USB_PID_AFATECH_AF9035_1003 0x1003
+#define USB_PID_AFATECH_AF9035_9035 0x9035
#define USB_PID_TREKSTOR_DVBT 0x901b
#define USB_VID_ALINK_DTU 0xf170
#define USB_PID_ANSONIC_DVBT_USB 0x6000
@@ -152,6 +157,7 @@
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055
#define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069
+#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093
#define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097
#define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099
#define USB_PID_TWINHAN_VP7041_COLD 0x3201
@@ -221,6 +227,11 @@
#define USB_PID_AVERMEDIA_A850T 0x850b
#define USB_PID_AVERMEDIA_A805 0xa805
#define USB_PID_AVERMEDIA_A815M 0x815a
+#define USB_PID_AVERMEDIA_A835 0xa835
+#define USB_PID_AVERMEDIA_B835 0xb835
+#define USB_PID_AVERMEDIA_1867 0x1867
+#define USB_PID_AVERMEDIA_A867 0xa867
+#define USB_PID_AVERMEDIA_TWINSTAR 0x0825
#define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006
#define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d
#define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a
@@ -327,6 +338,7 @@
#define USB_PID_MYGICA_D689 0xd811
#define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011
#define USB_PID_ELGATO_EYETV_DTT 0x0021
+#define USB_PID_ELGATO_EYETV_DTT_2 0x003f
#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
#define USB_PID_ELGATO_EYETV_SAT 0x002a
#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
index 53a5c30b51b..5c8f651344f 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
@@ -80,6 +80,14 @@ static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer
dvb_dmx_swfilter_204(&adap->demux, buffer, length);
}
+static void dvb_usb_data_complete_raw(struct usb_data_stream *stream,
+ u8 *buffer, size_t length)
+{
+ struct dvb_usb_adapter *adap = stream->user_priv;
+ if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB)
+ dvb_dmx_swfilter_raw(&adap->demux, buffer, length);
+}
+
int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap)
{
int i, ret = 0;
@@ -90,6 +98,10 @@ int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap)
adap->fe_adap[i].stream.complete =
dvb_usb_data_complete_204;
else
+ if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD)
+ adap->fe_adap[i].stream.complete =
+ dvb_usb_data_complete_raw;
+ else
adap->fe_adap[i].stream.complete = dvb_usb_data_complete;
adap->fe_adap[i].stream.user_priv = adap;
ret = usb_urb_init(&adap->fe_adap[i].stream,
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
index 6d7d13f9ce6..99f94409efa 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb.h
@@ -141,6 +141,7 @@ struct dvb_usb_adapter_fe_properties {
#define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02
#define DVB_USB_ADAP_NEED_PID_FILTERING 0x04
#define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08
+#define DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD 0x10
int caps;
int pid_filter_count;
@@ -156,7 +157,7 @@ struct dvb_usb_adapter_fe_properties {
int size_of_priv;
};
-#define MAX_NO_OF_FE_PER_ADAP 2
+#define MAX_NO_OF_FE_PER_ADAP 3
struct dvb_usb_adapter_properties {
int size_of_priv;
diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c
index 451c5a7adfb..9382895b1b8 100644
--- a/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/drivers/media/dvb/dvb-usb/dw2102.c
@@ -148,7 +148,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int i = 0, ret = 0;
+ int i = 0;
u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0};
u16 value;
@@ -162,7 +162,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
/* read stv0299 register */
value = msg[0].buf[0];/* register */
for (i = 0; i < msg[1].len; i++) {
- ret = dw210x_op_rw(d->udev, 0xb5, value + i, 0,
+ dw210x_op_rw(d->udev, 0xb5, value + i, 0,
buf6, 2, DW210X_READ_MSG);
msg[1].buf[i] = buf6[0];
}
@@ -174,7 +174,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
buf6[0] = 0x2a;
buf6[1] = msg[0].buf[0];
buf6[2] = msg[0].buf[1];
- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ dw210x_op_rw(d->udev, 0xb2, 0, 0,
buf6, 3, DW210X_WRITE_MSG);
break;
case 0x60:
@@ -187,17 +187,17 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
buf6[4] = msg[0].buf[1];
buf6[5] = msg[0].buf[2];
buf6[6] = msg[0].buf[3];
- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ dw210x_op_rw(d->udev, 0xb2, 0, 0,
buf6, 7, DW210X_WRITE_MSG);
} else {
/* read from tuner */
- ret = dw210x_op_rw(d->udev, 0xb5, 0, 0,
+ dw210x_op_rw(d->udev, 0xb5, 0, 0,
buf6, 1, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
}
break;
case (DW2102_RC_QUERY):
- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ dw210x_op_rw(d->udev, 0xb8, 0, 0,
buf6, 2, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
msg[0].buf[1] = buf6[1];
@@ -205,7 +205,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
case (DW2102_VOLTAGE_CTRL):
buf6[0] = 0x30;
buf6[1] = msg[0].buf[0];
- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ dw210x_op_rw(d->udev, 0xb2, 0, 0,
buf6, 2, DW210X_WRITE_MSG);
break;
}
@@ -221,7 +221,6 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
struct i2c_msg msg[], int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret = 0;
u8 buf6[] = {0, 0, 0, 0, 0, 0, 0};
if (!d)
@@ -235,10 +234,10 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
buf6[0] = msg[0].addr << 1;
buf6[1] = msg[0].len;
buf6[2] = msg[0].buf[0];
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
buf6, msg[0].len + 2, DW210X_WRITE_MSG);
/* read si2109 register */
- ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0,
+ dw210x_op_rw(d->udev, 0xc3, 0xd0, 0,
buf6, msg[1].len + 2, DW210X_READ_MSG);
memcpy(msg[1].buf, buf6 + 2, msg[1].len);
@@ -250,11 +249,11 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
buf6[0] = msg[0].addr << 1;
buf6[1] = msg[0].len;
memcpy(buf6 + 2, msg[0].buf, msg[0].len);
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6,
msg[0].len + 2, DW210X_WRITE_MSG);
break;
case(DW2102_RC_QUERY):
- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ dw210x_op_rw(d->udev, 0xb8, 0, 0,
buf6, 2, DW210X_READ_MSG);
msg[0].buf[0] = buf6[0];
msg[0].buf[1] = buf6[1];
@@ -262,7 +261,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
case(DW2102_VOLTAGE_CTRL):
buf6[0] = 0x30;
buf6[1] = msg[0].buf[0];
- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ dw210x_op_rw(d->udev, 0xb2, 0, 0,
buf6, 2, DW210X_WRITE_MSG);
break;
}
@@ -276,7 +275,6 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap,
static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret = 0;
if (!d)
return -ENODEV;
@@ -291,10 +289,10 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
obuf[2] = msg[0].buf[0];
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
obuf, msg[0].len + 2, DW210X_WRITE_MSG);
/* second read registers */
- ret = dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0,
+ dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0,
ibuf, msg[1].len + 2, DW210X_READ_MSG);
memcpy(msg[1].buf, ibuf + 2, msg[1].len);
@@ -308,7 +306,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
obuf, msg[0].len + 2, DW210X_WRITE_MSG);
break;
}
@@ -318,13 +316,13 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
obuf, msg[0].len + 2, DW210X_WRITE_MSG);
break;
}
case(DW2102_RC_QUERY): {
u8 ibuf[2];
- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ dw210x_op_rw(d->udev, 0xb8, 0, 0,
ibuf, 2, DW210X_READ_MSG);
memcpy(msg[0].buf, ibuf , 2);
break;
@@ -333,7 +331,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
u8 obuf[2];
obuf[0] = 0x30;
obuf[1] = msg[0].buf[0];
- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ dw210x_op_rw(d->udev, 0xb2, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
break;
}
@@ -349,7 +347,6 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms
static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret = 0;
int len, i, j;
if (!d)
@@ -361,7 +358,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
switch (msg[j].addr) {
case(DW2102_RC_QUERY): {
u8 ibuf[2];
- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ dw210x_op_rw(d->udev, 0xb8, 0, 0,
ibuf, 2, DW210X_READ_MSG);
memcpy(msg[j].buf, ibuf , 2);
break;
@@ -370,7 +367,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
u8 obuf[2];
obuf[0] = 0x30;
obuf[1] = msg[j].buf[0];
- ret = dw210x_op_rw(d->udev, 0xb2, 0, 0,
+ dw210x_op_rw(d->udev, 0xb2, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
break;
}
@@ -382,7 +379,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
if (msg[j].flags == I2C_M_RD) {
/* read registers */
u8 ibuf[msg[j].len + 2];
- ret = dw210x_op_rw(d->udev, 0xc3,
+ dw210x_op_rw(d->udev, 0xc3,
(msg[j].addr << 1) + 1, 0,
ibuf, msg[j].len + 2,
DW210X_READ_MSG);
@@ -402,7 +399,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
do {
memcpy(obuf + 3, msg[j].buf + i,
(len > 16 ? 16 : len));
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
obuf, (len > 16 ? 16 : len) + 3,
DW210X_WRITE_MSG);
i += 16;
@@ -414,7 +411,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i
obuf[0] = msg[j].addr << 1;
obuf[1] = msg[j].len;
memcpy(obuf + 2, msg[j].buf, msg[j].len);
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
obuf, msg[j].len + 2,
DW210X_WRITE_MSG);
}
@@ -432,7 +429,7 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int num)
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
- int ret = 0, i;
+ int i;
if (!d)
return -ENODEV;
@@ -447,10 +444,10 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
obuf[2] = msg[0].buf[0];
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
obuf, msg[0].len + 2, DW210X_WRITE_MSG);
/* second read registers */
- ret = dw210x_op_rw(d->udev, 0xc3, 0x19 , 0,
+ dw210x_op_rw(d->udev, 0xc3, 0x19 , 0,
ibuf, msg[1].len + 2, DW210X_READ_MSG);
memcpy(msg[1].buf, ibuf + 2, msg[1].len);
@@ -465,13 +462,13 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = msg[0].addr << 1;
obuf[1] = msg[0].len;
memcpy(obuf + 2, msg[0].buf, msg[0].len);
- ret = dw210x_op_rw(d->udev, 0xc2, 0, 0,
+ dw210x_op_rw(d->udev, 0xc2, 0, 0,
obuf, msg[0].len + 2, DW210X_WRITE_MSG);
break;
}
case(DW2102_RC_QUERY): {
u8 ibuf[2];
- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ dw210x_op_rw(d->udev, 0xb8, 0, 0,
ibuf, 2, DW210X_READ_MSG);
memcpy(msg[0].buf, ibuf , 2);
break;
@@ -496,7 +493,6 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
{
struct dvb_usb_device *d = i2c_get_adapdata(adap);
struct usb_device *udev;
- int ret = 0;
int len, i, j;
if (!d)
@@ -509,7 +505,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
switch (msg[j].addr) {
case (DW2102_RC_QUERY): {
u8 ibuf[5];
- ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ dw210x_op_rw(d->udev, 0xb8, 0, 0,
ibuf, 5, DW210X_READ_MSG);
memcpy(msg[j].buf, ibuf + 3, 2);
break;
@@ -519,11 +515,11 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = 1;
obuf[1] = msg[j].buf[1];/* off-on */
- ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
+ dw210x_op_rw(d->udev, 0x8a, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
obuf[0] = 3;
obuf[1] = msg[j].buf[0];/* 13v-18v */
- ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
+ dw210x_op_rw(d->udev, 0x8a, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
break;
}
@@ -532,7 +528,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = 5;
obuf[1] = msg[j].buf[0];
- ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
+ dw210x_op_rw(d->udev, 0x8a, 0, 0,
obuf, 2, DW210X_WRITE_MSG);
break;
}
@@ -545,7 +541,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
if (msg[j].flags == I2C_M_RD) {
/* read registers */
u8 ibuf[msg[j].len];
- ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
+ dw210x_op_rw(d->udev, 0x91, 0, 0,
ibuf, msg[j].len,
DW210X_READ_MSG);
memcpy(msg[j].buf, ibuf, msg[j].len);
@@ -563,7 +559,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
do {
memcpy(obuf + 3, msg[j].buf + i,
(len > 16 ? 16 : len));
- ret = dw210x_op_rw(d->udev, 0x80, 0, 0,
+ dw210x_op_rw(d->udev, 0x80, 0, 0,
obuf, (len > 16 ? 16 : len) + 3,
DW210X_WRITE_MSG);
i += 16;
@@ -575,7 +571,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = msg[j + 1].len;
obuf[1] = (msg[j].addr << 1);
memcpy(obuf + 2, msg[j].buf, msg[j].len);
- ret = dw210x_op_rw(d->udev,
+ dw210x_op_rw(d->udev,
udev->descriptor.idProduct ==
0x7500 ? 0x92 : 0x90, 0, 0,
obuf, msg[j].len + 2,
@@ -587,7 +583,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
obuf[0] = msg[j].len + 1;
obuf[1] = (msg[j].addr << 1);
memcpy(obuf + 2, msg[j].buf, msg[j].len);
- ret = dw210x_op_rw(d->udev, 0x80, 0, 0,
+ dw210x_op_rw(d->udev, 0x80, 0, 0,
obuf, msg[j].len + 2,
DW210X_WRITE_MSG);
break;
diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c
index 482d249ca7f..6244fe9d1a3 100644
--- a/drivers/media/dvb/dvb-usb/it913x.c
+++ b/drivers/media/dvb/dvb-usb/it913x.c
@@ -81,7 +81,7 @@ static int it913x_bulk_write(struct usb_device *dev,
for (i = 0; i < IT913X_RETRY; i++) {
ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe),
snd, len , &actual_l, IT913X_SND_TIMEOUT);
- if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT)
+ if (ret != -EBUSY && ret != -ETIMEDOUT)
break;
}
@@ -99,7 +99,7 @@ static int it913x_bulk_read(struct usb_device *dev,
for (i = 0; i < IT913X_RETRY; i++) {
ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe),
rev, len , &actual_l, IT913X_RCV_TIMEOUT);
- if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT)
+ if (ret != -EBUSY && ret != -ETIMEDOUT)
break;
}
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
index 5dde06d066f..25d1031460f 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.c
+++ b/drivers/media/dvb/dvb-usb/lmedm04.c
@@ -373,7 +373,7 @@ static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
struct lme2510_state *st = adap->dev->priv;
static u8 clear_pid_reg[] = LME_ALL_PIDS;
static u8 rbuf[1];
- int ret;
+ int ret = 0;
deb_info(1, "PID Clearing Filter");
@@ -1205,14 +1205,13 @@ static int lme2510_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
- int ret = 0;
usb_reset_configuration(udev);
usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1);
if (udev->speed != USB_SPEED_HIGH) {
- ret = usb_reset_device(udev);
+ usb_reset_device(udev);
info("DEV Failed to connect in HIGH SPEED mode");
return -ENODEV;
}
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
index 72db6eef4b9..74da5bb1ce9 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
+++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c
@@ -284,6 +284,7 @@ static int mxl111sf_tuner_set_params(struct dvb_frontend *fe)
switch (delsys) {
case SYS_ATSC:
+ case SYS_ATSCMH:
bw = 0; /* ATSC */
break;
case SYS_DVBC_ANNEX_B:
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c
index 81305de2fea..cd842798f5a 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf.c
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.c
@@ -21,6 +21,7 @@
#include "mxl111sf-tuner.h"
#include "lgdt3305.h"
+#include "lg2160.h"
int dvb_usb_mxl111sf_debug;
module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644);
@@ -31,6 +32,10 @@ int dvb_usb_mxl111sf_isoc;
module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644);
MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc).");
+int dvb_usb_mxl111sf_spi;
+module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644);
+MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi).");
+
#define ANT_PATH_AUTO 0
#define ANT_PATH_EXTERNAL 1
#define ANT_PATH_INTERNAL 2
@@ -340,7 +345,6 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
struct mxl111sf_state *state = d->priv;
struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv;
int ret = 0;
- u8 tmp;
deb_info("%s(%d)\n", __func__, onoff);
@@ -361,6 +365,33 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
return ret;
}
+static int mxl111sf_ep5_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int ret = 0;
+
+ deb_info("%s(%d)\n", __func__, onoff);
+
+ if (onoff) {
+ ret = mxl111sf_enable_usb_output(state);
+ mxl_fail(ret);
+
+ ret = mxl111sf_init_i2s_port(state, 200);
+ mxl_fail(ret);
+ ret = mxl111sf_config_i2s(state, 0, 15);
+ mxl_fail(ret);
+ } else {
+ ret = mxl111sf_disable_i2s_port(state);
+ mxl_fail(ret);
+ }
+ if (state->chip_rev > MXL111SF_V6)
+ ret = mxl111sf_config_spi(state, onoff);
+ mxl_fail(ret);
+
+ return ret;
+}
+
static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct dvb_usb_device *d = adap->dev;
@@ -453,6 +484,255 @@ fail:
return ret;
}
+static struct lg2160_config hauppauge_lg2160_config = {
+ .lg_chip = LG2160,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+};
+
+static int mxl111sf_lg2160_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_MH;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach,
+ &hauppauge_lg2160_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[fe_id].fe) {
+ adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+ adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+ adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct lg2160_config hauppauge_lg2161_1019_config = {
+ .lg_chip = LG2161_1019,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 2, /* LG2161_OIF_SPI_MAS */
+};
+
+static struct lg2160_config hauppauge_lg2161_1040_config = {
+ .lg_chip = LG2161_1040,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 4, /* LG2161_OIF_SPI_MAS */
+};
+
+static int mxl111sf_lg2161_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_MH;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 1;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach,
+ (MXL111SF_V8_200 == state->chip_rev) ?
+ &hauppauge_lg2161_1040_config :
+ &hauppauge_lg2161_1019_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[fe_id].fe) {
+ adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+ adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+ adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
+static struct lg2160_config hauppauge_lg2161_1019_ep6_config = {
+ .lg_chip = LG2161_1019,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 1, /* LG2161_OIF_SERIAL_TS */
+};
+
+static struct lg2160_config hauppauge_lg2161_1040_ep6_config = {
+ .lg_chip = LG2161_1040,
+ .i2c_addr = 0x1c >> 1,
+ .deny_i2c_rptr = 1,
+ .spectral_inversion = 0,
+ .if_khz = 6000,
+ .output_if = 7, /* LG2161_OIF_SERIAL_TS */
+};
+
+static int mxl111sf_lg2161_ep6_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct dvb_usb_device *d = adap->dev;
+ struct mxl111sf_state *state = d->priv;
+ int fe_id = adap->num_frontends_initialized;
+ struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv;
+ int ret;
+
+ deb_adv("%s()\n", __func__);
+
+ /* save a pointer to the dvb_usb_device in device state */
+ state->d = d;
+ adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1;
+ state->alt_mode = adap_state->alt_mode;
+
+ if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0)
+ err("set interface failed");
+
+ state->gpio_mode = MXL111SF_GPIO_MOD_MH;
+ adap_state->gpio_mode = state->gpio_mode;
+ adap_state->device_mode = MXL_TUNER_MODE;
+ adap_state->ep6_clockphase = 0;
+
+ ret = mxl1x1sf_soft_reset(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_init_tuner_demod(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_enable_usb_output(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl1x1sf_top_master_ctrl(state, 1);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = mxl111sf_init_port_expander(state);
+ if (mxl_fail(ret))
+ goto fail;
+ ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode);
+ if (mxl_fail(ret))
+ goto fail;
+
+ ret = get_chip_info(state);
+ if (mxl_fail(ret))
+ goto fail;
+
+ adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach,
+ (MXL111SF_V8_200 == state->chip_rev) ?
+ &hauppauge_lg2161_1040_ep6_config :
+ &hauppauge_lg2161_1019_ep6_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[fe_id].fe) {
+ adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init;
+ adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init;
+ adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep;
+ adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep;
+ return 0;
+ }
+ ret = -EIO;
+fail:
+ return ret;
+}
+
static struct mxl111sf_demod_config mxl_demod_config = {
.read_reg = mxl111sf_read_reg,
.write_reg = mxl111sf_write_reg,
@@ -650,6 +930,18 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties;
static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties;
static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties;
static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties;
+static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties;
static int mxl111sf_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -664,12 +956,50 @@ static int mxl111sf_probe(struct usb_interface *intf,
THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf,
&mxl111sf_atsc_isoc_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_atsc_mh_isoc_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mh_isoc_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ ((dvb_usb_mxl111sf_spi) &&
+ (0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_spi_isoc_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_mh_spi_isoc_properties,
+ THIS_MODULE, &d, adapter_nr))) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_tp_isoc_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_mh_tp_isoc_properties,
THIS_MODULE, &d, adapter_nr))) ||
0 == dvb_usb_device_init(intf,
&mxl111sf_dvbt_bulk_properties,
THIS_MODULE, &d, adapter_nr) ||
0 == dvb_usb_device_init(intf,
&mxl111sf_atsc_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_atsc_mh_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mh_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ ((dvb_usb_mxl111sf_spi) &&
+ (0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_spi_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_mh_spi_bulk_properties,
+ THIS_MODULE, &d, adapter_nr))) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_tp_bulk_properties,
+ THIS_MODULE, &d, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf,
+ &mxl111sf_mercury_mh_tp_bulk_properties,
THIS_MODULE, &d, adapter_nr) || 0) {
struct mxl111sf_state *state = d->priv;
@@ -787,6 +1117,36 @@ MODULE_DEVICE_TABLE(usb, mxl111sf_table);
} \
}
+#define MXL111SF_EP5_BULK_STREAMING_CONFIG \
+ .size_of_priv = sizeof(struct mxl111sf_adap_state), \
+ .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \
+ .stream = { \
+ .type = USB_BULK, \
+ .count = 5, \
+ .endpoint = 0x05, \
+ .u = { \
+ .bulk = { \
+ .buffersize = 8192, \
+ } \
+ } \
+ }
+
+#define MXL111SF_EP5_ISOC_STREAMING_CONFIG \
+ .size_of_priv = sizeof(struct mxl111sf_adap_state), \
+ .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \
+ .stream = { \
+ .type = USB_ISOC, \
+ .count = 5, \
+ .endpoint = 0x05, \
+ .u = { \
+ .isoc = { \
+ .framesperurb = 96, \
+ .framesize = 200, \
+ .interval = 1, \
+ } \
+ } \
+ }
+
#define MXL111SF_EP6_BULK_STREAMING_CONFIG \
.size_of_priv = sizeof(struct mxl111sf_adap_state), \
.streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \
@@ -848,7 +1208,7 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = {
} },
},
},
- .num_device_descs = 4,
+ .num_device_descs = 3,
.devices = {
{ "Hauppauge 126xxx DVBT (bulk)",
{ NULL },
@@ -866,11 +1226,6 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = {
&mxl111sf_table[24], &mxl111sf_table[26],
NULL },
},
- { "Hauppauge 126xxx (tp-bulk)",
- { NULL },
- { &mxl111sf_table[28], &mxl111sf_table[30],
- NULL },
- },
}
};
@@ -890,7 +1245,7 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = {
} },
},
},
- .num_device_descs = 4,
+ .num_device_descs = 3,
.devices = {
{ "Hauppauge 126xxx DVBT (isoc)",
{ NULL },
@@ -908,11 +1263,6 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = {
&mxl111sf_table[24], &mxl111sf_table[26],
NULL },
},
- { "Hauppauge 126xxx (tp-isoc)",
- { NULL },
- { &mxl111sf_table[28], &mxl111sf_table[30],
- NULL },
- },
}
};
@@ -923,33 +1273,159 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
.adapter = {
{
.fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 2,
+ .num_frontends = 1,
.fe = {{
.frontend_attach = mxl111sf_lgdt3305_frontend_attach,
.tuner_attach = mxl111sf_attach_tuner,
MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ }},
},
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "Hauppauge 126xxx ATSC (bulk)",
+ { NULL },
+ { &mxl111sf_table[1], &mxl111sf_table[5],
+ NULL },
+ },
+ { "Hauppauge 117xxx ATSC (bulk)",
+ { NULL },
+ { &mxl111sf_table[12],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
{
- .frontend_attach = mxl111sf_attach_demod,
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 1,
+ .fe = {{
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
.tuner_attach = mxl111sf_attach_tuner,
- MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ MXL111SF_EP6_ISOC_STREAMING_CONFIG,
}},
},
},
- .num_device_descs = 6,
+ .num_device_descs = 2,
.devices = {
- { "Hauppauge 126xxx ATSC (bulk)",
+ { "Hauppauge 126xxx ATSC (isoc)",
{ NULL },
{ &mxl111sf_table[1], &mxl111sf_table[5],
NULL },
},
- { "Hauppauge 117xxx ATSC (bulk)",
+ { "Hauppauge 117xxx ATSC (isoc)",
{ NULL },
{ &mxl111sf_table[12],
NULL },
},
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 1,
+ .fe = {{
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2160_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "HCW 126xxx (bulk)",
+ { NULL },
+ { &mxl111sf_table[2], &mxl111sf_table[6],
+ NULL },
+ },
+ { "HCW 117xxx (bulk)",
+ { NULL },
+ { &mxl111sf_table[13],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 1,
+ .fe = {{
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2160_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_ISOC_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "HCW 126xxx (isoc)",
+ { NULL },
+ { &mxl111sf_table[2], &mxl111sf_table[6],
+ NULL },
+ },
+ { "HCW 117xxx (isoc)",
+ { NULL },
+ { &mxl111sf_table[13],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 3,
+ .fe = {{
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ },
+ {
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2160_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 2,
+ .devices = {
{ "Hauppauge 126xxx ATSC+ (bulk)",
{ NULL },
{ &mxl111sf_table[0], &mxl111sf_table[3],
@@ -963,13 +1439,96 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
&mxl111sf_table[32], &mxl111sf_table[33],
NULL },
},
- { "Hauppauge Mercury (tp-bulk)",
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 3,
+ .fe = {{
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2160_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_ISOC_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "Hauppauge 126xxx ATSC+ (isoc)",
+ { NULL },
+ { &mxl111sf_table[0], &mxl111sf_table[3],
+ &mxl111sf_table[7], &mxl111sf_table[9],
+ &mxl111sf_table[10], NULL },
+ },
+ { "Hauppauge 117xxx ATSC+ (isoc)",
+ { NULL },
+ { &mxl111sf_table[11], &mxl111sf_table[14],
+ &mxl111sf_table[16], &mxl111sf_table[17],
+ &mxl111sf_table[32], &mxl111sf_table[33],
+ NULL },
+ },
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 3,
+ .fe = {{
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ },
+ {
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "Hauppauge Mercury (spi-bulk)",
{ NULL },
{ &mxl111sf_table[19], &mxl111sf_table[21],
&mxl111sf_table[23], &mxl111sf_table[25],
- &mxl111sf_table[27], NULL },
+ NULL },
},
- { "Hauppauge WinTV-Aero-M",
+ { "Hauppauge WinTV-Aero-M (spi-bulk)",
{ NULL },
{ &mxl111sf_table[29], &mxl111sf_table[31],
NULL },
@@ -977,14 +1536,14 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = {
}
};
-static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
+static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties = {
MXL111SF_DEFAULT_DEVICE_PROPERTIES,
.num_adapters = 1,
.adapter = {
{
.fe_ioctl_override = mxl111sf_fe_ioctl_override,
- .num_frontends = 2,
+ .num_frontends = 3,
.fe = {{
.frontend_attach = mxl111sf_lgdt3305_frontend_attach,
.tuner_attach = mxl111sf_attach_tuner,
@@ -996,34 +1555,111 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
.tuner_attach = mxl111sf_attach_tuner,
MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_ISOC_STREAMING_CONFIG,
}},
},
},
- .num_device_descs = 6,
+ .num_device_descs = 2,
.devices = {
- { "Hauppauge 126xxx ATSC (isoc)",
+ { "Hauppauge Mercury (spi-isoc)",
{ NULL },
- { &mxl111sf_table[1], &mxl111sf_table[5],
+ { &mxl111sf_table[19], &mxl111sf_table[21],
+ &mxl111sf_table[23], &mxl111sf_table[25],
NULL },
},
- { "Hauppauge 117xxx ATSC (isoc)",
+ { "Hauppauge WinTV-Aero-M (spi-isoc)",
{ NULL },
- { &mxl111sf_table[12],
+ { &mxl111sf_table[29], &mxl111sf_table[31],
NULL },
},
- { "Hauppauge 126xxx ATSC+ (isoc)",
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 3,
+ .fe = {{
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ },
+ {
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 2,
+ .devices = {
+ { "Hauppauge Mercury (tp-bulk)",
{ NULL },
- { &mxl111sf_table[0], &mxl111sf_table[3],
- &mxl111sf_table[7], &mxl111sf_table[9],
- &mxl111sf_table[10], NULL },
+ { &mxl111sf_table[19], &mxl111sf_table[21],
+ &mxl111sf_table[23], &mxl111sf_table[25],
+ &mxl111sf_table[27], NULL },
},
- { "Hauppauge 117xxx ATSC+ (isoc)",
+ { "Hauppauge WinTV-Aero-M",
{ NULL },
- { &mxl111sf_table[11], &mxl111sf_table[14],
- &mxl111sf_table[16], &mxl111sf_table[17],
- &mxl111sf_table[32], &mxl111sf_table[33],
+ { &mxl111sf_table[29], &mxl111sf_table[31],
NULL },
},
+ }
+};
+
+static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 3,
+ .fe = {{
+ .frontend_attach = mxl111sf_lgdt3305_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_ISOC_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 2,
+ .devices = {
{ "Hauppauge Mercury (tp-isoc)",
{ NULL },
{ &mxl111sf_table[19], &mxl111sf_table[21],
@@ -1038,6 +1674,146 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = {
}
};
+static
+struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 1,
+ .devices = {
+ { "Hauppauge 126xxx (tp-bulk)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
+static
+struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP6_ISOC_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 1,
+ .devices = {
+ { "Hauppauge 126xxx (tp-isoc)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
+static
+struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_BULK_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_BULK_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 1,
+ .devices = {
+ { "Hauppauge 126xxx (spi-bulk)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
+static
+struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties = {
+ MXL111SF_DEFAULT_DEVICE_PROPERTIES,
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .fe_ioctl_override = mxl111sf_fe_ioctl_override,
+ .num_frontends = 2,
+ .fe = {{
+ .frontend_attach = mxl111sf_attach_demod,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP4_ISOC_STREAMING_CONFIG,
+ },
+ {
+ .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD,
+
+ .frontend_attach = mxl111sf_lg2161_frontend_attach,
+ .tuner_attach = mxl111sf_attach_tuner,
+
+ MXL111SF_EP5_ISOC_STREAMING_CONFIG,
+ }},
+ },
+ },
+ .num_device_descs = 1,
+ .devices = {
+ { "Hauppauge 126xxx (spi-isoc)",
+ { NULL },
+ { &mxl111sf_table[28], &mxl111sf_table[30],
+ NULL },
+ },
+ }
+};
+
static struct usb_driver mxl111sf_driver = {
.name = "dvb_usb_mxl111sf",
.probe = mxl111sf_probe,
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c
index 8f4736a10fc..41e1f5537f4 100644
--- a/drivers/media/dvb/dvb-usb/rtl28xxu.c
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c
@@ -322,6 +322,9 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
* since there is some demod params needed to set according to tuner.
*/
+ /* demod needs some time to wake up */
+ msleep(20);
+
/* open demod I2C gate */
ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
if (ret)
@@ -909,6 +912,8 @@ static int rtl28xxu_probe(struct usb_interface *intf,
int ret, i;
int properties_count = ARRAY_SIZE(rtl28xxu_properties);
struct dvb_usb_device *d;
+ struct usb_device *udev;
+ bool found;
deb_info("%s: interface=%d\n", __func__,
intf->cur_altsetting->desc.bInterfaceNumber);
@@ -916,6 +921,29 @@ static int rtl28xxu_probe(struct usb_interface *intf,
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
return 0;
+ /* Dynamic USB ID support. Replaces first device ID with current one .*/
+ udev = interface_to_usbdev(intf);
+
+ for (i = 0, found = false; i < ARRAY_SIZE(rtl28xxu_table) - 1; i++) {
+ if (rtl28xxu_table[i].idVendor ==
+ le16_to_cpu(udev->descriptor.idVendor) &&
+ rtl28xxu_table[i].idProduct ==
+ le16_to_cpu(udev->descriptor.idProduct)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ deb_info("%s: using dynamic ID %04x:%04x\n", __func__,
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+ rtl28xxu_properties[0].devices[0].warm_ids[0]->idVendor =
+ le16_to_cpu(udev->descriptor.idVendor);
+ rtl28xxu_properties[0].devices[0].warm_ids[0]->idProduct =
+ le16_to_cpu(udev->descriptor.idProduct);
+ }
+
for (i = 0; i < properties_count; i++) {
ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i],
THIS_MODULE, &d, adapter_nr);
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index 21246707fbf..b98ebb264e2 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -531,6 +531,14 @@ config DVB_LGDT3305
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
+config DVB_LG2160
+ tristate "LG Electronics LG216x based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ An ATSC/MH demodulator module. Say Y when you want
+ to support this frontend.
+
config DVB_S5H1409
tristate "Samsung S5H1409 based"
depends on DVB_CORE && I2C
@@ -540,12 +548,26 @@ config DVB_S5H1409
to support this frontend.
config DVB_AU8522
- tristate "Auvitek AU8522 based"
- depends on DVB_CORE && I2C && VIDEO_V4L2
+ depends on I2C
+ tristate
+
+config DVB_AU8522_DTV
+ tristate "Auvitek AU8522 based DTV demod"
+ depends on DVB_CORE && I2C
+ select DVB_AU8522
default m if DVB_FE_CUSTOMISE
help
- An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
- to support this frontend.
+ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
+ you want to enable DTV demodulation support for this frontend.
+
+config DVB_AU8522_V4L
+ tristate "Auvitek AU8522 based ATV demod"
+ depends on VIDEO_V4L2 && I2C
+ select DVB_AU8522
+ default m if DVB_FE_CUSTOMISE
+ help
+ An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when
+ you want to enable ATV demodulation support for this frontend.
config DVB_S5H1411
tristate "Samsung S5H1411 based"
@@ -713,6 +735,11 @@ config DVB_M88RS2000
A DVB-S tuner module.
Say Y when you want to support this frontend.
+config DVB_AF9033
+ tristate "Afatech AF9033 DVB-T demodulator"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+
comment "Tools to develop new frontends"
config DVB_DUMMY_FE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 86fa808bf58..cd1ac2fd577 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -7,7 +7,6 @@ ccflags-y += -I$(srctree)/drivers/media/common/tuners/
stb0899-objs = stb0899_drv.o stb0899_algo.o
stv0900-objs = stv0900_core.o stv0900_sw.o
-au8522-objs = au8522_dig.o au8522_decoder.o
drxd-objs = drxd_firm.o drxd_hard.o
cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o
drxk-objs := drxk_hard.o
@@ -50,6 +49,7 @@ obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
+obj-$(CONFIG_DVB_LG2160) += lg2160.o
obj-$(CONFIG_DVB_CX24123) += cx24123.o
obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
obj-$(CONFIG_DVB_LNBP22) += lnbp22.o
@@ -63,7 +63,9 @@ obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.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_AU8522) += au8522_common.o
+obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o
+obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o
obj-$(CONFIG_DVB_TDA10048) += tda10048.o
obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o
obj-$(CONFIG_DVB_S5H1411) += s5h1411.o
@@ -98,4 +100,5 @@ obj-$(CONFIG_DVB_A8293) += a8293.o
obj-$(CONFIG_DVB_TDA10071) += tda10071.o
obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
+obj-$(CONFIG_DVB_AF9033) += af9033.o
diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c
index 6bcbcf543b3..5bc570d7784 100644
--- a/drivers/media/dvb/frontends/af9013.c
+++ b/drivers/media/dvb/frontends/af9013.c
@@ -514,7 +514,6 @@ err:
static void af9013_statistics_work(struct work_struct *work)
{
- int ret;
struct af9013_state *state = container_of(work,
struct af9013_state, statistics_work.work);
unsigned int next_msec;
@@ -530,27 +529,27 @@ static void af9013_statistics_work(struct work_struct *work)
default:
state->statistics_step = 0;
case 0:
- ret = af9013_statistics_signal_strength(&state->fe);
+ af9013_statistics_signal_strength(&state->fe);
state->statistics_step++;
next_msec = 300;
break;
case 1:
- ret = af9013_statistics_snr_start(&state->fe);
+ af9013_statistics_snr_start(&state->fe);
state->statistics_step++;
next_msec = 200;
break;
case 2:
- ret = af9013_statistics_ber_unc_start(&state->fe);
+ af9013_statistics_ber_unc_start(&state->fe);
state->statistics_step++;
next_msec = 1000;
break;
case 3:
- ret = af9013_statistics_snr_result(&state->fe);
+ af9013_statistics_snr_result(&state->fe);
state->statistics_step++;
next_msec = 400;
break;
case 4:
- ret = af9013_statistics_ber_unc_result(&state->fe);
+ af9013_statistics_ber_unc_result(&state->fe);
state->statistics_step++;
next_msec = 100;
break;
@@ -558,8 +557,6 @@ static void af9013_statistics_work(struct work_struct *work)
schedule_delayed_work(&state->statistics_work,
msecs_to_jiffies(next_msec));
-
- return;
}
static int af9013_get_tune_settings(struct dvb_frontend *fe,
diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c
new file mode 100644
index 00000000000..a3899828626
--- /dev/null
+++ b/drivers/media/dvb/frontends/af9033.c
@@ -0,0 +1,980 @@
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "af9033_priv.h"
+
+struct af9033_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct af9033_config cfg;
+
+ u32 bandwidth_hz;
+ bool ts_mode_parallel;
+ bool ts_mode_serial;
+
+ u32 ber;
+ u32 ucb;
+ unsigned long last_stat_check;
+};
+
+/* write multiple registers */
+static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val,
+ int len)
+{
+ int ret;
+ u8 buf[3 + len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = state->cfg.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = (reg >> 16) & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ buf[2] = (reg >> 0) & 0xff;
+ memcpy(&buf[3], val, len);
+
+ ret = i2c_transfer(state->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ printk(KERN_WARNING "%s: i2c wr failed=%d reg=%06x len=%d\n",
+ __func__, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+/* read multiple registers */
+static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff,
+ (reg >> 0) & 0xff };
+ struct i2c_msg msg[2] = {
+ {
+ .addr = state->cfg.i2c_addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf
+ }, {
+ .addr = state->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ printk(KERN_WARNING "%s: i2c rd failed=%d reg=%06x len=%d\n",
+ __func__, ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+
+ return ret;
+}
+
+
+/* write single register */
+static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val)
+{
+ return af9033_wr_regs(state, reg, &val, 1);
+}
+
+/* read single register */
+static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val)
+{
+ return af9033_rd_regs(state, reg, val, 1);
+}
+
+/* write single register with mask */
+static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val,
+ u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = af9033_rd_regs(state, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return af9033_wr_regs(state, reg, &val, 1);
+}
+
+/* read single register with mask */
+static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val,
+ u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = af9033_rd_regs(state, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static u32 af9033_div(u32 a, u32 b, u32 x)
+{
+ u32 r = 0, c = 0, i;
+
+ pr_debug("%s: a=%d b=%d x=%d\n", __func__, a, b, x);
+
+ if (a > b) {
+ c = a / b;
+ a = a - c * b;
+ }
+
+ for (i = 0; i < x; i++) {
+ if (a >= b) {
+ r += 1;
+ a -= b;
+ }
+ a <<= 1;
+ r <<= 1;
+ }
+ r = (c << (u32)x) + r;
+
+ pr_debug("%s: a=%d b=%d x=%d r=%d r=%x\n", __func__, a, b, x, r, r);
+
+ return r;
+}
+
+static void af9033_release(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+
+ kfree(state);
+}
+
+static int af9033_init(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ const struct reg_val *init;
+ u8 buf[4];
+ u32 adc_cw, clock_cw;
+ struct reg_val_mask tab[] = {
+ { 0x80fb24, 0x00, 0x08 },
+ { 0x80004c, 0x00, 0xff },
+ { 0x00f641, state->cfg.tuner, 0xff },
+ { 0x80f5ca, 0x01, 0x01 },
+ { 0x80f715, 0x01, 0x01 },
+ { 0x00f41f, 0x04, 0x04 },
+ { 0x00f41a, 0x01, 0x01 },
+ { 0x80f731, 0x00, 0x01 },
+ { 0x00d91e, 0x00, 0x01 },
+ { 0x00d919, 0x00, 0x01 },
+ { 0x80f732, 0x00, 0x01 },
+ { 0x00d91f, 0x00, 0x01 },
+ { 0x00d91a, 0x00, 0x01 },
+ { 0x80f730, 0x00, 0x01 },
+ { 0x80f778, 0x00, 0xff },
+ { 0x80f73c, 0x01, 0x01 },
+ { 0x80f776, 0x00, 0x01 },
+ { 0x00d8fd, 0x01, 0xff },
+ { 0x00d830, 0x01, 0xff },
+ { 0x00d831, 0x00, 0xff },
+ { 0x00d832, 0x00, 0xff },
+ { 0x80f985, state->ts_mode_serial, 0x01 },
+ { 0x80f986, state->ts_mode_parallel, 0x01 },
+ { 0x00d827, 0x00, 0xff },
+ { 0x00d829, 0x00, 0xff },
+ };
+
+ /* program clock control */
+ clock_cw = af9033_div(state->cfg.clock, 1000000ul, 19ul);
+ buf[0] = (clock_cw >> 0) & 0xff;
+ buf[1] = (clock_cw >> 8) & 0xff;
+ buf[2] = (clock_cw >> 16) & 0xff;
+ buf[3] = (clock_cw >> 24) & 0xff;
+
+ pr_debug("%s: clock=%d clock_cw=%08x\n", __func__, state->cfg.clock,
+ clock_cw);
+
+ ret = af9033_wr_regs(state, 0x800025, buf, 4);
+ if (ret < 0)
+ goto err;
+
+ /* program ADC control */
+ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
+ if (clock_adc_lut[i].clock == state->cfg.clock)
+ break;
+ }
+
+ adc_cw = af9033_div(clock_adc_lut[i].adc, 1000000ul, 19ul);
+ buf[0] = (adc_cw >> 0) & 0xff;
+ buf[1] = (adc_cw >> 8) & 0xff;
+ buf[2] = (adc_cw >> 16) & 0xff;
+
+ pr_debug("%s: adc=%d adc_cw=%06x\n", __func__, clock_adc_lut[i].adc,
+ adc_cw);
+
+ ret = af9033_wr_regs(state, 0x80f1cd, buf, 3);
+ if (ret < 0)
+ goto err;
+
+ /* program register table */
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* settings for TS interface */
+ if (state->cfg.ts_mode == AF9033_TS_MODE_USB) {
+ ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+ } else {
+ ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* load OFSM settings */
+ pr_debug("%s: load ofsm settings\n", __func__);
+ len = ARRAY_SIZE(ofsm_init);
+ init = ofsm_init;
+ for (i = 0; i < len; i++) {
+ ret = af9033_wr_reg(state, init[i].reg, init[i].val);
+ if (ret < 0)
+ goto err;
+ }
+
+ /* load tuner specific settings */
+ pr_debug("%s: load tuner specific settings\n",
+ __func__);
+ switch (state->cfg.tuner) {
+ case AF9033_TUNER_TUA9001:
+ len = ARRAY_SIZE(tuner_init_tua9001);
+ init = tuner_init_tua9001;
+ break;
+ case AF9033_TUNER_FC0011:
+ len = ARRAY_SIZE(tuner_init_fc0011);
+ init = tuner_init_fc0011;
+ break;
+ case AF9033_TUNER_MXL5007T:
+ len = ARRAY_SIZE(tuner_init_mxl5007t);
+ init = tuner_init_mxl5007t;
+ break;
+ case AF9033_TUNER_TDA18218:
+ len = ARRAY_SIZE(tuner_init_tda18218);
+ init = tuner_init_tda18218;
+ break;
+ default:
+ pr_debug("%s: unsupported tuner ID=%d\n", __func__,
+ state->cfg.tuner);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = af9033_wr_reg(state, init[i].reg, init[i].val);
+ if (ret < 0)
+ goto err;
+ }
+
+ state->bandwidth_hz = 0; /* force to program all parameters */
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_sleep(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret, i;
+ u8 tmp;
+
+ ret = af9033_wr_reg(state, 0x80004c, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800000, 0);
+ if (ret < 0)
+ goto err;
+
+ for (i = 100, tmp = 1; i && tmp; i--) {
+ ret = af9033_rd_reg(state, 0x80004c, &tmp);
+ if (ret < 0)
+ goto err;
+
+ usleep_range(200, 10000);
+ }
+
+ pr_debug("%s: loop=%d\n", __func__, i);
+
+ if (i == 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08);
+ if (ret < 0)
+ goto err;
+
+ /* prevent current leak (?) */
+ if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) {
+ /* enable parallel TS */
+ ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *fesettings)
+{
+ fesettings->min_delay_ms = 800;
+ fesettings->step_size = 0;
+ fesettings->max_drift = 0;
+
+ return 0;
+}
+
+static int af9033_set_frontend(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i, spec_inv;
+ u8 tmp, buf[3], bandwidth_reg_val;
+ u32 if_frequency, freq_cw, adc_freq;
+
+ pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency,
+ c->bandwidth_hz);
+
+ /* check bandwidth */
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ bandwidth_reg_val = 0x00;
+ break;
+ case 7000000:
+ bandwidth_reg_val = 0x01;
+ break;
+ case 8000000:
+ bandwidth_reg_val = 0x02;
+ break;
+ default:
+ pr_debug("%s: invalid bandwidth_hz\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ /* program CFOE coefficients */
+ if (c->bandwidth_hz != state->bandwidth_hz) {
+ for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) {
+ if (coeff_lut[i].clock == state->cfg.clock &&
+ coeff_lut[i].bandwidth_hz == c->bandwidth_hz) {
+ break;
+ }
+ }
+ ret = af9033_wr_regs(state, 0x800001,
+ coeff_lut[i].val, sizeof(coeff_lut[i].val));
+ }
+
+ /* program frequency control */
+ if (c->bandwidth_hz != state->bandwidth_hz) {
+ spec_inv = state->cfg.spec_inv ? -1 : 1;
+
+ for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) {
+ if (clock_adc_lut[i].clock == state->cfg.clock)
+ break;
+ }
+ adc_freq = clock_adc_lut[i].adc;
+
+ /* get used IF frequency */
+ if (fe->ops.tuner_ops.get_if_frequency)
+ fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency);
+ else
+ if_frequency = 0;
+
+ while (if_frequency > (adc_freq / 2))
+ if_frequency -= adc_freq;
+
+ if (if_frequency >= 0)
+ spec_inv *= -1;
+ else
+ if_frequency *= -1;
+
+ freq_cw = af9033_div(if_frequency, adc_freq, 23ul);
+
+ if (spec_inv == -1)
+ freq_cw *= -1;
+
+ /* get adc multiplies */
+ ret = af9033_rd_reg(state, 0x800045, &tmp);
+ if (ret < 0)
+ goto err;
+
+ if (tmp == 1)
+ freq_cw /= 2;
+
+ buf[0] = (freq_cw >> 0) & 0xff;
+ buf[1] = (freq_cw >> 8) & 0xff;
+ buf[2] = (freq_cw >> 16) & 0x7f;
+ ret = af9033_wr_regs(state, 0x800029, buf, 3);
+ if (ret < 0)
+ goto err;
+
+ state->bandwidth_hz = c->bandwidth_hz;
+ }
+
+ ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800040, 0x00);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800047, 0x00);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01);
+ if (ret < 0)
+ goto err;
+
+ if (c->frequency <= 230000000)
+ tmp = 0x00; /* VHF */
+ else
+ tmp = 0x01; /* UHF */
+
+ ret = af9033_wr_reg(state, 0x80004b, tmp);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_wr_reg(state, 0x800000, 0x00);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_get_frontend(struct dvb_frontend *fe)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[8];
+
+ pr_debug("%s\n", __func__);
+
+ /* read all needed registers */
+ ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf));
+ if (ret < 0)
+ goto err;
+
+ switch ((buf[0] >> 0) & 3) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[2] >> 0) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[3] >> 0) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[4] >> 0) & 3) {
+ case 0:
+ c->bandwidth_hz = 6000000;
+ break;
+ case 1:
+ c->bandwidth_hz = 7000000;
+ break;
+ case 2:
+ c->bandwidth_hz = 8000000;
+ break;
+ }
+
+ switch ((buf[6] >> 0) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ case 5:
+ c->code_rate_HP = FEC_NONE;
+ break;
+ }
+
+ switch ((buf[7] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ case 5:
+ c->code_rate_LP = FEC_NONE;
+ break;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+
+ *status = 0;
+
+ /* radio channel status, 0=no result, 1=has signal, 2=no signal */
+ ret = af9033_rd_reg(state, 0x800047, &tmp);
+ if (ret < 0)
+ goto err;
+
+ /* has signal */
+ if (tmp == 0x01)
+ *status |= FE_HAS_SIGNAL;
+
+ if (tmp != 0x02) {
+ /* TPS lock */
+ ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01);
+ if (ret < 0)
+ goto err;
+
+ if (tmp)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+
+ /* full lock */
+ ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01);
+ if (ret < 0)
+ goto err;
+
+ if (tmp)
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_LOCK;
+ }
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret, i, len;
+ u8 buf[3], tmp;
+ u32 snr_val;
+ const struct val_snr *uninitialized_var(snr_lut);
+
+ /* read value */
+ ret = af9033_rd_regs(state, 0x80002c, buf, 3);
+ if (ret < 0)
+ goto err;
+
+ snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ /* read current modulation */
+ ret = af9033_rd_reg(state, 0x80f903, &tmp);
+ if (ret < 0)
+ goto err;
+
+ switch ((tmp >> 0) & 3) {
+ case 0:
+ len = ARRAY_SIZE(qpsk_snr_lut);
+ snr_lut = qpsk_snr_lut;
+ break;
+ case 1:
+ len = ARRAY_SIZE(qam16_snr_lut);
+ snr_lut = qam16_snr_lut;
+ break;
+ case 2:
+ len = ARRAY_SIZE(qam64_snr_lut);
+ snr_lut = qam64_snr_lut;
+ break;
+ default:
+ goto err;
+ }
+
+ for (i = 0; i < len; i++) {
+ tmp = snr_lut[i].snr;
+
+ if (snr_val < snr_lut[i].val)
+ break;
+ }
+
+ *snr = tmp * 10; /* dB/10 */
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+ u8 strength2;
+
+ /* read signal strength of 0-100 scale */
+ ret = af9033_rd_reg(state, 0x800048, &strength2);
+ if (ret < 0)
+ goto err;
+
+ /* scale value to 0x0000-0xffff */
+ *strength = strength2 * 0xffff / 100;
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static int af9033_update_ch_stat(struct af9033_state *state)
+{
+ int ret = 0;
+ u32 err_cnt, bit_cnt;
+ u16 abort_cnt;
+ u8 buf[7];
+
+ /* only update data every half second */
+ if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) {
+ ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf));
+ if (ret < 0)
+ goto err;
+ /* in 8 byte packets? */
+ abort_cnt = (buf[1] << 8) + buf[0];
+ /* in bits */
+ err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2];
+ /* in 8 byte packets? always(?) 0x2710 = 10000 */
+ bit_cnt = (buf[6] << 8) + buf[5];
+
+ if (bit_cnt < abort_cnt) {
+ abort_cnt = 1000;
+ state->ber = 0xffffffff;
+ } else {
+ /* 8 byte packets, that have not been rejected already */
+ bit_cnt -= (u32)abort_cnt;
+ if (bit_cnt == 0) {
+ state->ber = 0xffffffff;
+ } else {
+ err_cnt -= (u32)abort_cnt * 8 * 8;
+ bit_cnt *= 8 * 8;
+ state->ber = err_cnt * (0xffffffff / bit_cnt);
+ }
+ }
+ state->ucb += abort_cnt;
+ state->last_stat_check = jiffies;
+ }
+
+ return 0;
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+
+ ret = af9033_update_ch_stat(state);
+ if (ret < 0)
+ return ret;
+
+ *ber = state->ber;
+
+ return 0;
+}
+
+static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+
+ ret = af9033_update_ch_stat(state);
+ if (ret < 0)
+ return ret;
+
+ *ucblocks = state->ucb;
+
+ return 0;
+}
+
+static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct af9033_state *state = fe->demodulator_priv;
+ int ret;
+
+ pr_debug("%s: enable=%d\n", __func__, enable);
+
+ ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ pr_debug("%s: failed=%d\n", __func__, ret);
+
+ return ret;
+}
+
+static struct dvb_frontend_ops af9033_ops;
+
+struct dvb_frontend *af9033_attach(const struct af9033_config *config,
+ struct i2c_adapter *i2c)
+{
+ int ret;
+ struct af9033_state *state;
+ u8 buf[8];
+
+ pr_debug("%s:\n", __func__);
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL);
+ if (state == NULL)
+ goto err;
+
+ /* setup the state */
+ state->i2c = i2c;
+ memcpy(&state->cfg, config, sizeof(struct af9033_config));
+
+ if (state->cfg.clock != 12000000) {
+ printk(KERN_INFO "af9033: unsupported clock=%d, only " \
+ "12000000 Hz is supported currently\n",
+ state->cfg.clock);
+ goto err;
+ }
+
+ /* firmware version */
+ ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4);
+ if (ret < 0)
+ goto err;
+
+ ret = af9033_rd_regs(state, 0x804191, &buf[4], 4);
+ if (ret < 0)
+ goto err;
+
+ printk(KERN_INFO "af9033: firmware version: LINK=%d.%d.%d.%d " \
+ "OFDM=%d.%d.%d.%d\n", buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ /* configure internal TS mode */
+ switch (state->cfg.ts_mode) {
+ case AF9033_TS_MODE_PARALLEL:
+ state->ts_mode_parallel = true;
+ break;
+ case AF9033_TS_MODE_SERIAL:
+ state->ts_mode_serial = true;
+ break;
+ case AF9033_TS_MODE_USB:
+ /* usb mode for AF9035 */
+ default:
+ break;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops));
+ state->fe.demodulator_priv = state;
+
+ return &state->fe;
+
+err:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(af9033_attach);
+
+static struct dvb_frontend_ops af9033_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Afatech AF9033 (DVB-T)",
+ .frequency_min = 174000000,
+ .frequency_max = 862000000,
+ .frequency_stepsize = 250000,
+ .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
+ },
+
+ .release = af9033_release,
+
+ .init = af9033_init,
+ .sleep = af9033_sleep,
+
+ .get_tune_settings = af9033_get_tune_settings,
+ .set_frontend = af9033_set_frontend,
+ .get_frontend = af9033_get_frontend,
+
+ .read_status = af9033_read_status,
+ .read_snr = af9033_read_snr,
+ .read_signal_strength = af9033_read_signal_strength,
+ .read_ber = af9033_read_ber,
+ .read_ucblocks = af9033_read_ucblocks,
+
+ .i2c_gate_ctrl = af9033_i2c_gate_ctrl,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/af9033.h b/drivers/media/dvb/frontends/af9033.h
new file mode 100644
index 00000000000..9e302c3f0f7
--- /dev/null
+++ b/drivers/media/dvb/frontends/af9033.h
@@ -0,0 +1,75 @@
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef AF9033_H
+#define AF9033_H
+
+struct af9033_config {
+ /*
+ * I2C address
+ */
+ u8 i2c_addr;
+
+ /*
+ * clock Hz
+ * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000,
+ * 30000000, 36000000, 20480000, 16384000
+ */
+ u32 clock;
+
+ /*
+ * tuner
+ */
+#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */
+#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */
+#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */
+#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */
+ u8 tuner;
+
+ /*
+ * TS settings
+ */
+#define AF9033_TS_MODE_USB 0
+#define AF9033_TS_MODE_PARALLEL 1
+#define AF9033_TS_MODE_SERIAL 2
+ u8 ts_mode:2;
+
+ /*
+ * input spectrum inversion
+ */
+ bool spec_inv;
+};
+
+
+#if defined(CONFIG_DVB_AF9033) || \
+ (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE))
+extern struct dvb_frontend *af9033_attach(const struct af9033_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *af9033_attach(
+ const struct af9033_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif
+
+#endif /* AF9033_H */
diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h
new file mode 100644
index 00000000000..0b783b9ed75
--- /dev/null
+++ b/drivers/media/dvb/frontends/af9033_priv.h
@@ -0,0 +1,470 @@
+/*
+ * Afatech AF9033 demodulator driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2012 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef AF9033_PRIV_H
+#define AF9033_PRIV_H
+
+#include "dvb_frontend.h"
+#include "af9033.h"
+
+struct reg_val {
+ u32 reg;
+ u8 val;
+};
+
+struct reg_val_mask {
+ u32 reg;
+ u8 val;
+ u8 mask;
+};
+
+struct coeff {
+ u32 clock;
+ u32 bandwidth_hz;
+ u8 val[36];
+};
+
+struct clock_adc {
+ u32 clock;
+ u32 adc;
+};
+
+struct val_snr {
+ u32 val;
+ u8 snr;
+};
+
+/* Xtal clock vs. ADC clock lookup table */
+static const struct clock_adc clock_adc_lut[] = {
+ { 16384000, 20480000 },
+ { 20480000, 20480000 },
+ { 36000000, 20250000 },
+ { 30000000, 20156250 },
+ { 26000000, 20583333 },
+ { 28000000, 20416667 },
+ { 32000000, 20500000 },
+ { 34000000, 20187500 },
+ { 24000000, 20500000 },
+ { 22000000, 20625000 },
+ { 12000000, 20250000 },
+};
+
+/* pre-calculated coeff lookup table */
+static const struct coeff coeff_lut[] = {
+ /* 12.000 MHz */
+ { 12000000, 8000000, {
+ 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
+ 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5,
+ 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73,
+ 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 }
+ },
+ { 12000000, 7000000, {
+ 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
+ 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b,
+ 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65,
+ 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 }
+ },
+ { 12000000, 6000000, {
+ 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
+ 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60,
+ 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56,
+ 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 }
+ },
+};
+
+/* QPSK SNR lookup table */
+static const struct val_snr qpsk_snr_lut[] = {
+ { 0x0b4771, 0 },
+ { 0x0c1aed, 1 },
+ { 0x0d0d27, 2 },
+ { 0x0e4d19, 3 },
+ { 0x0e5da8, 4 },
+ { 0x107097, 5 },
+ { 0x116975, 6 },
+ { 0x1252d9, 7 },
+ { 0x131fa4, 8 },
+ { 0x13d5e1, 9 },
+ { 0x148e53, 10 },
+ { 0x15358b, 11 },
+ { 0x15dd29, 12 },
+ { 0x168112, 13 },
+ { 0x170b61, 14 },
+ { 0x17a532, 15 },
+ { 0x180f94, 16 },
+ { 0x186ed2, 17 },
+ { 0x18b271, 18 },
+ { 0x18e118, 19 },
+ { 0x18ff4b, 20 },
+ { 0x190af1, 21 },
+ { 0x191451, 22 },
+ { 0xffffff, 23 },
+};
+
+/* QAM16 SNR lookup table */
+static const struct val_snr qam16_snr_lut[] = {
+ { 0x04f0d5, 0 },
+ { 0x05387a, 1 },
+ { 0x0573a4, 2 },
+ { 0x05a99e, 3 },
+ { 0x05cc80, 4 },
+ { 0x05eb62, 5 },
+ { 0x05fecf, 6 },
+ { 0x060b80, 7 },
+ { 0x062501, 8 },
+ { 0x064865, 9 },
+ { 0x069604, 10 },
+ { 0x06f356, 11 },
+ { 0x07706a, 12 },
+ { 0x0804d3, 13 },
+ { 0x089d1a, 14 },
+ { 0x093e3d, 15 },
+ { 0x09e35d, 16 },
+ { 0x0a7c3c, 17 },
+ { 0x0afaf8, 18 },
+ { 0x0b719d, 19 },
+ { 0x0bda6a, 20 },
+ { 0x0c0c75, 21 },
+ { 0x0c3f7d, 22 },
+ { 0x0c5e62, 23 },
+ { 0x0c6c31, 24 },
+ { 0x0c7925, 25 },
+ { 0xffffff, 26 },
+};
+
+/* QAM64 SNR lookup table */
+static const struct val_snr qam64_snr_lut[] = {
+ { 0x0256d0, 0 },
+ { 0x027a65, 1 },
+ { 0x029873, 2 },
+ { 0x02b7fe, 3 },
+ { 0x02cf1e, 4 },
+ { 0x02e234, 5 },
+ { 0x02f409, 6 },
+ { 0x030046, 7 },
+ { 0x030844, 8 },
+ { 0x030a02, 9 },
+ { 0x030cde, 10 },
+ { 0x031031, 11 },
+ { 0x03144c, 12 },
+ { 0x0315dd, 13 },
+ { 0x031920, 14 },
+ { 0x0322d0, 15 },
+ { 0x0339fc, 16 },
+ { 0x0364a1, 17 },
+ { 0x038bcc, 18 },
+ { 0x03c7d3, 19 },
+ { 0x0408cc, 20 },
+ { 0x043bed, 21 },
+ { 0x048061, 22 },
+ { 0x04be95, 23 },
+ { 0x04fa7d, 24 },
+ { 0x052405, 25 },
+ { 0x05570d, 26 },
+ { 0x059feb, 27 },
+ { 0x05bf38, 28 },
+ { 0xffffff, 29 },
+};
+
+static const struct reg_val ofsm_init[] = {
+ { 0x800051, 0x01 },
+ { 0x800070, 0x0a },
+ { 0x80007e, 0x04 },
+ { 0x800081, 0x0a },
+ { 0x80008a, 0x01 },
+ { 0x80008e, 0x01 },
+ { 0x800092, 0x06 },
+ { 0x800099, 0x01 },
+ { 0x80009f, 0xe1 },
+ { 0x8000a0, 0xcf },
+ { 0x8000a3, 0x01 },
+ { 0x8000a5, 0x01 },
+ { 0x8000a6, 0x01 },
+ { 0x8000a9, 0x00 },
+ { 0x8000aa, 0x01 },
+ { 0x8000ab, 0x01 },
+ { 0x8000b0, 0x01 },
+ { 0x8000c0, 0x05 },
+ { 0x8000c4, 0x19 },
+ { 0x80f000, 0x0f },
+ { 0x80f016, 0x10 },
+ { 0x80f017, 0x04 },
+ { 0x80f018, 0x05 },
+ { 0x80f019, 0x04 },
+ { 0x80f01a, 0x05 },
+ { 0x80f021, 0x03 },
+ { 0x80f022, 0x0a },
+ { 0x80f023, 0x0a },
+ { 0x80f02b, 0x00 },
+ { 0x80f02c, 0x01 },
+ { 0x80f064, 0x03 },
+ { 0x80f065, 0xf9 },
+ { 0x80f066, 0x03 },
+ { 0x80f067, 0x01 },
+ { 0x80f06f, 0xe0 },
+ { 0x80f070, 0x03 },
+ { 0x80f072, 0x0f },
+ { 0x80f073, 0x03 },
+ { 0x80f078, 0x00 },
+ { 0x80f087, 0x00 },
+ { 0x80f09b, 0x3f },
+ { 0x80f09c, 0x00 },
+ { 0x80f09d, 0x20 },
+ { 0x80f09e, 0x00 },
+ { 0x80f09f, 0x0c },
+ { 0x80f0a0, 0x00 },
+ { 0x80f130, 0x04 },
+ { 0x80f132, 0x04 },
+ { 0x80f144, 0x1a },
+ { 0x80f146, 0x00 },
+ { 0x80f14a, 0x01 },
+ { 0x80f14c, 0x00 },
+ { 0x80f14d, 0x00 },
+ { 0x80f14f, 0x04 },
+ { 0x80f158, 0x7f },
+ { 0x80f15a, 0x00 },
+ { 0x80f15b, 0x08 },
+ { 0x80f15d, 0x03 },
+ { 0x80f15e, 0x05 },
+ { 0x80f163, 0x05 },
+ { 0x80f166, 0x01 },
+ { 0x80f167, 0x40 },
+ { 0x80f168, 0x0f },
+ { 0x80f17a, 0x00 },
+ { 0x80f17b, 0x00 },
+ { 0x80f183, 0x01 },
+ { 0x80f19d, 0x40 },
+ { 0x80f1bc, 0x36 },
+ { 0x80f1bd, 0x00 },
+ { 0x80f1cb, 0xa0 },
+ { 0x80f1cc, 0x01 },
+ { 0x80f204, 0x10 },
+ { 0x80f214, 0x00 },
+ { 0x80f40e, 0x0a },
+ { 0x80f40f, 0x40 },
+ { 0x80f410, 0x08 },
+ { 0x80f55f, 0x0a },
+ { 0x80f561, 0x15 },
+ { 0x80f562, 0x20 },
+ { 0x80f5df, 0xfb },
+ { 0x80f5e0, 0x00 },
+ { 0x80f5e3, 0x09 },
+ { 0x80f5e4, 0x01 },
+ { 0x80f5e5, 0x01 },
+ { 0x80f5f8, 0x01 },
+ { 0x80f5fd, 0x01 },
+ { 0x80f600, 0x05 },
+ { 0x80f601, 0x08 },
+ { 0x80f602, 0x0b },
+ { 0x80f603, 0x0e },
+ { 0x80f604, 0x11 },
+ { 0x80f605, 0x14 },
+ { 0x80f606, 0x17 },
+ { 0x80f607, 0x1f },
+ { 0x80f60e, 0x00 },
+ { 0x80f60f, 0x04 },
+ { 0x80f610, 0x32 },
+ { 0x80f611, 0x10 },
+ { 0x80f707, 0xfc },
+ { 0x80f708, 0x00 },
+ { 0x80f709, 0x37 },
+ { 0x80f70a, 0x00 },
+ { 0x80f78b, 0x01 },
+ { 0x80f80f, 0x40 },
+ { 0x80f810, 0x54 },
+ { 0x80f811, 0x5a },
+ { 0x80f905, 0x01 },
+ { 0x80fb06, 0x03 },
+ { 0x80fd8b, 0x00 },
+};
+
+/* Infineon TUA 9001 tuner init
+ AF9033_TUNER_TUA9001 = 0x27 */
+static const struct reg_val tuner_init_tua9001[] = {
+ { 0x800046, 0x27 },
+ { 0x800057, 0x00 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x80006d, 0x00 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800075, 0x03 },
+ { 0x800076, 0x02 },
+ { 0x800077, 0x00 },
+ { 0x800078, 0x01 },
+ { 0x800079, 0x00 },
+ { 0x80007a, 0x7e },
+ { 0x80007b, 0x3e },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x01 },
+ { 0x800095, 0x02 },
+ { 0x800096, 0x01 },
+ { 0x800098, 0x0a },
+ { 0x80009b, 0x05 },
+ { 0x80009c, 0x80 },
+ { 0x8000b3, 0x00 },
+ { 0x8000c1, 0x01 },
+ { 0x8000c2, 0x00 },
+ { 0x80f007, 0x00 },
+ { 0x80f01f, 0x82 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x82 },
+ { 0x80f02a, 0x00 },
+ { 0x80f047, 0x00 },
+ { 0x80f054, 0x00 },
+ { 0x80f055, 0x00 },
+ { 0x80f077, 0x01 },
+ { 0x80f1e6, 0x00 },
+};
+
+/* Fitipower fc0011 tuner init
+ AF9033_TUNER_FC0011 = 0x28 */
+static const struct reg_val tuner_init_fc0011[] = {
+ { 0x800046, AF9033_TUNER_FC0011 },
+ { 0x800057, 0x00 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0xa5 },
+ { 0x80006e, 0x01 },
+ { 0x800071, 0x0A },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x80009b, 0x2D },
+ { 0x80009c, 0x60 },
+ { 0x80009d, 0x23 },
+ { 0x8000a4, 0x50 },
+ { 0x8000ad, 0x50 },
+ { 0x8000b3, 0x01 },
+ { 0x8000b7, 0x88 },
+ { 0x8000b8, 0xa6 },
+ { 0x8000c3, 0x01 },
+ { 0x8000c4, 0x01 },
+ { 0x8000c7, 0x69 },
+ { 0x80F007, 0x00 },
+ { 0x80F00A, 0x1B },
+ { 0x80F00B, 0x1B },
+ { 0x80F00C, 0x1B },
+ { 0x80F00D, 0x1B },
+ { 0x80F00E, 0xFF },
+ { 0x80F00F, 0x01 },
+ { 0x80F010, 0x00 },
+ { 0x80F011, 0x02 },
+ { 0x80F012, 0xFF },
+ { 0x80F013, 0x01 },
+ { 0x80F014, 0x00 },
+ { 0x80F015, 0x02 },
+ { 0x80F01B, 0xEF },
+ { 0x80F01C, 0x01 },
+ { 0x80F01D, 0x0f },
+ { 0x80F01E, 0x02 },
+ { 0x80F01F, 0x6E },
+ { 0x80F020, 0x00 },
+ { 0x80F025, 0xDE },
+ { 0x80F026, 0x00 },
+ { 0x80F027, 0x0A },
+ { 0x80F028, 0x03 },
+ { 0x80F029, 0x6E },
+ { 0x80F02A, 0x00 },
+ { 0x80F047, 0x00 },
+ { 0x80F054, 0x00 },
+ { 0x80F055, 0x00 },
+ { 0x80F077, 0x01 },
+ { 0x80F1E6, 0x00 },
+};
+
+/* MaxLinear MxL5007T tuner init
+ AF9033_TUNER_MXL5007T = 0xa0 */
+static const struct reg_val tuner_init_mxl5007t[] = {
+ { 0x800046, 0x1b },
+ { 0x800057, 0x01 },
+ { 0x800058, 0x01 },
+ { 0x80005f, 0x00 },
+ { 0x800060, 0x00 },
+ { 0x800068, 0x96 },
+ { 0x800071, 0x05 },
+ { 0x800072, 0x02 },
+ { 0x800074, 0x01 },
+ { 0x800079, 0x01 },
+ { 0x800093, 0x00 },
+ { 0x800094, 0x00 },
+ { 0x800095, 0x00 },
+ { 0x800096, 0x00 },
+ { 0x8000b3, 0x01 },
+ { 0x8000c1, 0x01 },
+ { 0x8000c2, 0x00 },
+ { 0x80f007, 0x00 },
+ { 0x80f00c, 0x19 },
+ { 0x80f00d, 0x1a },
+ { 0x80f012, 0xda },
+ { 0x80f013, 0x00 },
+ { 0x80f014, 0x00 },
+ { 0x80f015, 0x02 },
+ { 0x80f01f, 0x82 },
+ { 0x80f020, 0x00 },
+ { 0x80f029, 0x82 },
+ { 0x80f02a, 0x00 },
+ { 0x80f077, 0x02 },
+ { 0x80f1e6, 0x00 },
+};
+
+/* NXP TDA 18218HN tuner init
+ AF9033_TUNER_TDA18218 = 0xa1 */
+static const struct reg_val tuner_init_tda18218[] = {
+ {0x800046, 0xa1},
+ {0x800057, 0x01},
+ {0x800058, 0x01},
+ {0x80005f, 0x00},
+ {0x800060, 0x00},
+ {0x800071, 0x05},
+ {0x800072, 0x02},
+ {0x800074, 0x01},
+ {0x800079, 0x01},
+ {0x800093, 0x00},
+ {0x800094, 0x00},
+ {0x800095, 0x00},
+ {0x800096, 0x00},
+ {0x8000b3, 0x01},
+ {0x8000c3, 0x01},
+ {0x8000c4, 0x00},
+ {0x80f007, 0x00},
+ {0x80f00c, 0x19},
+ {0x80f00d, 0x1a},
+ {0x80f012, 0xda},
+ {0x80f013, 0x00},
+ {0x80f014, 0x00},
+ {0x80f015, 0x02},
+ {0x80f01f, 0x82},
+ {0x80f020, 0x00},
+ {0x80f029, 0x82},
+ {0x80f02a, 0x00},
+ {0x80f077, 0x02},
+ {0x80f1e6, 0x00},
+};
+
+#endif /* AF9033_PRIV_H */
+
diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb/frontends/au8522_common.c
new file mode 100644
index 00000000000..5cfe151ee39
--- /dev/null
+++ b/drivers/media/dvb/frontends/au8522_common.c
@@ -0,0 +1,259 @@
+/*
+ Auvitek AU8522 QAM/8VSB demodulator driver
+
+ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+ Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
+ Copyright (C) 2005-2008 Auvitek International, Ltd.
+ Copyright (C) 2012 Michael Krufky <mkrufky@linuxtv.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; 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/i2c.h>
+#include "dvb_frontend.h"
+#include "au8522_priv.h"
+
+MODULE_LICENSE("GPL");
+
+static int debug;
+
+#define dprintk(arg...)\
+ do { if (debug)\
+ printk(arg);\
+ } while (0)
+
+/* Despite the name "hybrid_tuner", the framework works just as well for
+ hybrid demodulators as well... */
+static LIST_HEAD(hybrid_tuner_instance_list);
+static DEFINE_MUTEX(au8522_list_mutex);
+
+/* 16 bit registers, 8 bit values */
+int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
+{
+ int ret;
+ u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
+
+ struct i2c_msg msg = { .addr = state->config->demod_address,
+ .flags = 0, .buf = buf, .len = 3 };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -1 : 0;
+}
+EXPORT_SYMBOL(au8522_writereg);
+
+u8 au8522_readreg(struct au8522_state *state, u16 reg)
+{
+ int ret;
+ u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
+ u8 b1[] = { 0 };
+
+ 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 = b1, .len = 1 } };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ printk(KERN_ERR "%s: readreg error (ret == %i)\n",
+ __func__, ret);
+ return b1[0];
+}
+EXPORT_SYMBOL(au8522_readreg);
+
+int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+
+ dprintk("%s(%d)\n", __func__, enable);
+
+ if (state->operational_mode == AU8522_ANALOG_MODE) {
+ /* We're being asked to manage the gate even though we're
+ not in digital mode. This can occur if we get switched
+ over to analog mode before the dvb_frontend kernel thread
+ has completely shutdown */
+ return 0;
+ }
+
+ if (enable)
+ return au8522_writereg(state, 0x106, 1);
+ else
+ return au8522_writereg(state, 0x106, 0);
+}
+EXPORT_SYMBOL(au8522_i2c_gate_ctrl);
+
+/* Reset the demod hardware and reset all of the configuration registers
+ to a default state. */
+int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
+ u8 client_address)
+{
+ int ret;
+
+ mutex_lock(&au8522_list_mutex);
+ ret = hybrid_tuner_request_state(struct au8522_state, (*state),
+ hybrid_tuner_instance_list,
+ i2c, client_address, "au8522");
+ mutex_unlock(&au8522_list_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL(au8522_get_state);
+
+void au8522_release_state(struct au8522_state *state)
+{
+ mutex_lock(&au8522_list_mutex);
+ if (state != NULL)
+ hybrid_tuner_release_state(state);
+ mutex_unlock(&au8522_list_mutex);
+}
+EXPORT_SYMBOL(au8522_release_state);
+
+static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ u8 val;
+
+ /* bail out if we can't control an LED */
+ if (!led_config || !led_config->gpio_output ||
+ !led_config->gpio_output_enable || !led_config->gpio_output_disable)
+ return 0;
+
+ val = au8522_readreg(state, 0x4000 |
+ (led_config->gpio_output & ~0xc000));
+ if (onoff) {
+ /* enable GPIO output */
+ val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
+ val |= (led_config->gpio_output_enable & 0xff);
+ } else {
+ /* disable GPIO output */
+ val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
+ val |= (led_config->gpio_output_disable & 0xff);
+ }
+ return au8522_writereg(state, 0x8000 |
+ (led_config->gpio_output & ~0xc000), val);
+}
+
+/* led = 0 | off
+ * led = 1 | signal ok
+ * led = 2 | signal strong
+ * led < 0 | only light led if leds are currently off
+ */
+int au8522_led_ctrl(struct au8522_state *state, int led)
+{
+ struct au8522_led_config *led_config = state->config->led_cfg;
+ int i, ret = 0;
+
+ /* bail out if we can't control an LED */
+ if (!led_config || !led_config->gpio_leds ||
+ !led_config->num_led_states || !led_config->led_states)
+ return 0;
+
+ if (led < 0) {
+ /* if LED is already lit, then leave it as-is */
+ if (state->led_state)
+ return 0;
+ else
+ led *= -1;
+ }
+
+ /* toggle LED if changing state */
+ if (state->led_state != led) {
+ u8 val;
+
+ dprintk("%s: %d\n", __func__, led);
+
+ au8522_led_gpio_enable(state, 1);
+
+ val = au8522_readreg(state, 0x4000 |
+ (led_config->gpio_leds & ~0xc000));
+
+ /* start with all leds off */
+ for (i = 0; i < led_config->num_led_states; i++)
+ val &= ~led_config->led_states[i];
+
+ /* set selected LED state */
+ if (led < led_config->num_led_states)
+ val |= led_config->led_states[led];
+ else if (led_config->num_led_states)
+ val |=
+ led_config->led_states[led_config->num_led_states - 1];
+
+ ret = au8522_writereg(state, 0x8000 |
+ (led_config->gpio_leds & ~0xc000), val);
+ if (ret < 0)
+ return ret;
+
+ state->led_state = led;
+
+ if (led == 0)
+ au8522_led_gpio_enable(state, 0);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(au8522_led_ctrl);
+
+int au8522_init(struct dvb_frontend *fe)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ dprintk("%s()\n", __func__);
+
+ state->operational_mode = AU8522_DIGITAL_MODE;
+
+ /* Clear out any state associated with the digital side of the
+ chip, so that when it gets powered back up it won't think
+ that it is already tuned */
+ state->current_frequency = 0;
+
+ au8522_writereg(state, 0xa4, 1 << 5);
+
+ au8522_i2c_gate_ctrl(fe, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL(au8522_init);
+
+int au8522_sleep(struct dvb_frontend *fe)
+{
+ struct au8522_state *state = fe->demodulator_priv;
+ dprintk("%s()\n", __func__);
+
+ /* Only power down if the digital side is currently using the chip */
+ if (state->operational_mode == AU8522_ANALOG_MODE) {
+ /* We're not in one of the expected power modes, which means
+ that the DVB thread is probably telling us to go to sleep
+ even though the analog frontend has already started using
+ the chip. So ignore the request */
+ return 0;
+ }
+
+ /* turn off led */
+ au8522_led_ctrl(state, 0);
+
+ /* Power down the chip */
+ au8522_writereg(state, 0xa4, 1 << 5);
+
+ state->current_frequency = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(au8522_sleep);
diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c
index 25f650934c7..5fc70d6cd04 100644
--- a/drivers/media/dvb/frontends/au8522_dig.c
+++ b/drivers/media/dvb/frontends/au8522_dig.c
@@ -30,74 +30,11 @@
static int debug;
-/* Despite the name "hybrid_tuner", the framework works just as well for
- hybrid demodulators as well... */
-static LIST_HEAD(hybrid_tuner_instance_list);
-static DEFINE_MUTEX(au8522_list_mutex);
-
#define dprintk(arg...)\
do { if (debug)\
printk(arg);\
} while (0)
-/* 16 bit registers, 8 bit values */
-int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
-{
- int ret;
- u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
-
- struct i2c_msg msg = { .addr = state->config->demod_address,
- .flags = 0, .buf = buf, .len = 3 };
-
- ret = i2c_transfer(state->i2c, &msg, 1);
-
- if (ret != 1)
- printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
- "ret == %i)\n", __func__, reg, data, ret);
-
- return (ret != 1) ? -1 : 0;
-}
-
-u8 au8522_readreg(struct au8522_state *state, u16 reg)
-{
- int ret;
- u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
- u8 b1[] = { 0 };
-
- 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 = b1, .len = 1 } };
-
- ret = i2c_transfer(state->i2c, msg, 2);
-
- if (ret != 2)
- printk(KERN_ERR "%s: readreg error (ret == %i)\n",
- __func__, ret);
- return b1[0];
-}
-
-static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
-{
- struct au8522_state *state = fe->demodulator_priv;
-
- dprintk("%s(%d)\n", __func__, enable);
-
- if (state->operational_mode == AU8522_ANALOG_MODE) {
- /* We're being asked to manage the gate even though we're
- not in digital mode. This can occur if we get switched
- over to analog mode before the dvb_frontend kernel thread
- has completely shutdown */
- return 0;
- }
-
- if (enable)
- return au8522_writereg(state, 0x106, 1);
- else
- return au8522_writereg(state, 0x106, 0);
-}
-
struct mse2snr_tab {
u16 val;
u16 data;
@@ -609,136 +546,6 @@ static int au8522_set_frontend(struct dvb_frontend *fe)
return 0;
}
-/* Reset the demod hardware and reset all of the configuration registers
- to a default state. */
-int au8522_init(struct dvb_frontend *fe)
-{
- struct au8522_state *state = fe->demodulator_priv;
- dprintk("%s()\n", __func__);
-
- state->operational_mode = AU8522_DIGITAL_MODE;
-
- /* Clear out any state associated with the digital side of the
- chip, so that when it gets powered back up it won't think
- that it is already tuned */
- state->current_frequency = 0;
-
- au8522_writereg(state, 0xa4, 1 << 5);
-
- au8522_i2c_gate_ctrl(fe, 1);
-
- return 0;
-}
-
-static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
-{
- struct au8522_led_config *led_config = state->config->led_cfg;
- u8 val;
-
- /* bail out if we can't control an LED */
- if (!led_config || !led_config->gpio_output ||
- !led_config->gpio_output_enable || !led_config->gpio_output_disable)
- return 0;
-
- val = au8522_readreg(state, 0x4000 |
- (led_config->gpio_output & ~0xc000));
- if (onoff) {
- /* enable GPIO output */
- val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
- val |= (led_config->gpio_output_enable & 0xff);
- } else {
- /* disable GPIO output */
- val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
- val |= (led_config->gpio_output_disable & 0xff);
- }
- return au8522_writereg(state, 0x8000 |
- (led_config->gpio_output & ~0xc000), val);
-}
-
-/* led = 0 | off
- * led = 1 | signal ok
- * led = 2 | signal strong
- * led < 0 | only light led if leds are currently off
- */
-static int au8522_led_ctrl(struct au8522_state *state, int led)
-{
- struct au8522_led_config *led_config = state->config->led_cfg;
- int i, ret = 0;
-
- /* bail out if we can't control an LED */
- if (!led_config || !led_config->gpio_leds ||
- !led_config->num_led_states || !led_config->led_states)
- return 0;
-
- if (led < 0) {
- /* if LED is already lit, then leave it as-is */
- if (state->led_state)
- return 0;
- else
- led *= -1;
- }
-
- /* toggle LED if changing state */
- if (state->led_state != led) {
- u8 val;
-
- dprintk("%s: %d\n", __func__, led);
-
- au8522_led_gpio_enable(state, 1);
-
- val = au8522_readreg(state, 0x4000 |
- (led_config->gpio_leds & ~0xc000));
-
- /* start with all leds off */
- for (i = 0; i < led_config->num_led_states; i++)
- val &= ~led_config->led_states[i];
-
- /* set selected LED state */
- if (led < led_config->num_led_states)
- val |= led_config->led_states[led];
- else if (led_config->num_led_states)
- val |=
- led_config->led_states[led_config->num_led_states - 1];
-
- ret = au8522_writereg(state, 0x8000 |
- (led_config->gpio_leds & ~0xc000), val);
- if (ret < 0)
- return ret;
-
- state->led_state = led;
-
- if (led == 0)
- au8522_led_gpio_enable(state, 0);
- }
-
- return 0;
-}
-
-int au8522_sleep(struct dvb_frontend *fe)
-{
- struct au8522_state *state = fe->demodulator_priv;
- dprintk("%s()\n", __func__);
-
- /* Only power down if the digital side is currently using the chip */
- if (state->operational_mode == AU8522_ANALOG_MODE) {
- /* We're not in one of the expected power modes, which means
- that the DVB thread is probably telling us to go to sleep
- even though the analog frontend has already started using
- the chip. So ignore the request */
- return 0;
- }
-
- /* turn off led */
- au8522_led_ctrl(state, 0);
-
- /* Power down the chip */
- au8522_writereg(state, 0xa4, 1 << 5);
-
- state->current_frequency = 0;
-
- return 0;
-}
-
static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct au8522_state *state = fe->demodulator_priv;
@@ -931,28 +738,6 @@ static int au8522_get_tune_settings(struct dvb_frontend *fe,
static struct dvb_frontend_ops au8522_ops;
-int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
- u8 client_address)
-{
- int ret;
-
- mutex_lock(&au8522_list_mutex);
- ret = hybrid_tuner_request_state(struct au8522_state, (*state),
- hybrid_tuner_instance_list,
- i2c, client_address, "au8522");
- mutex_unlock(&au8522_list_mutex);
-
- return ret;
-}
-
-void au8522_release_state(struct au8522_state *state)
-{
- mutex_lock(&au8522_list_mutex);
- if (state != NULL)
- hybrid_tuner_release_state(state);
- mutex_unlock(&au8522_list_mutex);
-}
-
static void au8522_release(struct dvb_frontend *fe)
{
diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h
index 751e17d692a..6e4a438732b 100644
--- a/drivers/media/dvb/frontends/au8522_priv.h
+++ b/drivers/media/dvb/frontends/au8522_priv.h
@@ -81,6 +81,8 @@ int au8522_sleep(struct dvb_frontend *fe);
int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
u8 client_address);
void au8522_release_state(struct au8522_state *state);
+int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable);
+int au8522_led_ctrl(struct au8522_state *state, int led);
/* REGISTERS */
#define AU8522_INPUT_CONTROL_REG081H 0x081
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
index 5101f10f2d7..98ecaf0900d 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -512,14 +512,13 @@ static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr)
static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
{
struct cx24110_state *state = fe->demodulator_priv;
- u32 lastbyer;
if(cx24110_readreg(state,0x10)&0x40) {
/* the RS error counter has finished one counting window */
cx24110_writereg(state,0x10,0x60); /* select the byer reg */
- lastbyer=cx24110_readreg(state,0x12)|
- (cx24110_readreg(state,0x13)<<8)|
- (cx24110_readreg(state,0x14)<<16);
+ cx24110_readreg(state, 0x12) |
+ (cx24110_readreg(state, 0x13) << 8) |
+ (cx24110_readreg(state, 0x14) << 16);
cx24110_writereg(state,0x10,0x70); /* select the bler reg */
state->lastbler=cx24110_readreg(state,0x12)|
(cx24110_readreg(state,0x13)<<8)|
diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c
index 5c7c2aaf9bf..3bba37d74f5 100644
--- a/drivers/media/dvb/frontends/cxd2820r_core.c
+++ b/drivers/media/dvb/frontends/cxd2820r_core.c
@@ -526,12 +526,12 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe)
if (ret)
goto error;
- if (status & FE_HAS_SIGNAL)
+ if (status & FE_HAS_LOCK)
break;
}
/* check if we have a valid signal */
- if (status) {
+ if (status & FE_HAS_LOCK) {
priv->last_tune_failed = 0;
return DVBFE_ALGO_SEARCH_SUCCESS;
} else {
diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c
index 5ceadc285b3..3e1eefada0e 100644
--- a/drivers/media/dvb/frontends/dib7000p.c
+++ b/drivers/media/dvb/frontends/dib7000p.c
@@ -2396,11 +2396,6 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
more common) */
st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
- /* FIXME: make sure the dev.parent field is initialized, or else
- request_firmware() will hit an OOPS (this should be moved somewhere
- more common) */
- st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent;
-
dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr);
/* init 7090 tuner adapter */
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c
index 80848b4c15d..6201c59a78d 100644
--- a/drivers/media/dvb/frontends/dib9000.c
+++ b/drivers/media/dvb/frontends/dib9000.c
@@ -31,13 +31,6 @@ struct i2c_device {
u8 *i2c_write_buffer;
};
-/* lock */
-#define DIB_LOCK struct mutex
-#define DibAcquireLock(lock) mutex_lock_interruptible(lock)
-#define DibReleaseLock(lock) mutex_unlock(lock)
-#define DibInitLock(lock) mutex_init(lock)
-#define DibFreeLock(lock)
-
struct dib9000_pid_ctrl {
#define DIB9000_PID_FILTER_CTRL 0
#define DIB9000_PID_FILTER 1
@@ -82,11 +75,11 @@ struct dib9000_state {
} fe_mm[18];
u8 memcmd;
- DIB_LOCK mbx_if_lock; /* to protect read/write operations */
- DIB_LOCK mbx_lock; /* to protect the whole mailbox handling */
+ struct mutex mbx_if_lock; /* to protect read/write operations */
+ struct mutex mbx_lock; /* to protect the whole mailbox handling */
- DIB_LOCK mem_lock; /* to protect the memory accesses */
- DIB_LOCK mem_mbx_lock; /* to protect the memory-based mailbox */
+ struct mutex mem_lock; /* to protect the memory accesses */
+ struct mutex mem_mbx_lock; /* to protect the memory-based mailbox */
#define MBX_MAX_WORDS (256 - 200 - 2)
#define DIB9000_MSG_CACHE_SIZE 2
@@ -108,7 +101,7 @@ struct dib9000_state {
struct i2c_msg msg[2];
u8 i2c_write_buffer[255];
u8 i2c_read_buffer[255];
- DIB_LOCK demod_lock;
+ struct mutex demod_lock;
u8 get_frontend_internal;
struct dib9000_pid_ctrl pid_ctrl[10];
s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */
@@ -446,13 +439,13 @@ static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u1
if (!state->platform.risc.fw_is_running)
return -EIO;
- if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
dib9000_risc_mem_setup(state, cmd | 0x80);
dib9000_risc_mem_read_chunks(state, b, len);
- DibReleaseLock(&state->platform.risc.mem_lock);
+ mutex_unlock(&state->platform.risc.mem_lock);
return 0;
}
@@ -462,13 +455,13 @@ static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8
if (!state->platform.risc.fw_is_running)
return -EIO;
- if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
dib9000_risc_mem_setup(state, cmd);
dib9000_risc_mem_write_chunks(state, b, m->size);
- DibReleaseLock(&state->platform.risc.mem_lock);
+ mutex_unlock(&state->platform.risc.mem_lock);
return 0;
}
@@ -537,7 +530,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data,
if (!state->platform.risc.fw_is_running)
return -EINVAL;
- if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -584,7 +577,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data,
ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr);
out:
- DibReleaseLock(&state->platform.risc.mbx_if_lock);
+ mutex_unlock(&state->platform.risc.mbx_if_lock);
return ret;
}
@@ -602,7 +595,7 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id,
if (!state->platform.risc.fw_is_running)
return 0;
- if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) {
dprintk("could not get the lock");
return 0;
}
@@ -643,7 +636,7 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id,
/* Update register nb_mes_in_TX */
dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr);
- DibReleaseLock(&state->platform.risc.mbx_if_lock);
+ mutex_unlock(&state->platform.risc.mbx_if_lock);
return size + 1;
}
@@ -708,12 +701,11 @@ static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr)
static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
{
int ret = 0;
- u16 tmp;
if (!state->platform.risc.fw_is_running)
return -1;
- if (DibAcquireLock(&state->platform.risc.mbx_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) {
dprintk("could not get the lock");
return -1;
}
@@ -721,10 +713,10 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */
ret = dib9000_mbx_fetch_to_cache(state, attr);
- tmp = dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */
+ dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */
/* if (tmp) */
/* dprintk( "cleared IRQ: %x", tmp); */
- DibReleaseLock(&state->platform.risc.mbx_lock);
+ mutex_unlock(&state->platform.risc.mbx_lock);
return ret;
}
@@ -1193,7 +1185,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe)
struct dibDVBTChannel *ch;
int ret = 0;
- if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -1323,7 +1315,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe)
}
error:
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
return ret;
}
@@ -1678,7 +1670,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2
p[12] = 0;
}
- if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
dprintk("could not get the lock");
return 0;
}
@@ -1692,7 +1684,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2
/* do the transaction */
if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) {
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
return 0;
}
@@ -1700,7 +1692,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2
if ((num > 1) && (msg[1].flags & I2C_M_RD))
dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len);
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
return num;
}
@@ -1789,7 +1781,7 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
return 0;
}
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -1799,7 +1791,7 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
dprintk("PID filter enabled %d", onoff);
ret = dib9000_write_word(state, 294 + 1, val);
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return ret;
}
@@ -1824,14 +1816,14 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
return 0;
}
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
ret = dib9000_write_word(state, 300 + 1 + id,
onoff ? (1 << 13) | pid : 0);
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return ret;
}
EXPORT_SYMBOL(dib9000_fw_pid_filter);
@@ -1851,11 +1843,6 @@ static void dib9000_release(struct dvb_frontend *demod)
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++)
dvb_frontend_detach(st->fe[index_frontend]);
- DibFreeLock(&state->platform.risc.mbx_if_lock);
- DibFreeLock(&state->platform.risc.mbx_lock);
- DibFreeLock(&state->platform.risc.mem_lock);
- DibFreeLock(&state->platform.risc.mem_mbx_lock);
- DibFreeLock(&state->demod_lock);
dibx000_exit_i2c_master(&st->i2c_master);
i2c_del_adapter(&st->tuner_adap);
@@ -1875,7 +1862,7 @@ static int dib9000_sleep(struct dvb_frontend *fe)
u8 index_frontend;
int ret = 0;
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -1887,7 +1874,7 @@ static int dib9000_sleep(struct dvb_frontend *fe)
ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0);
error:
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return ret;
}
@@ -1905,7 +1892,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe)
int ret = 0;
if (state->get_frontend_internal == 0) {
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -1964,7 +1951,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe)
return_value:
if (state->get_frontend_internal == 0)
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return ret;
}
@@ -2012,7 +1999,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe)
}
state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return 0;
}
@@ -2081,7 +2068,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe)
/* check the tune result */
if (exit_condition == 1) { /* tune failed */
dprintk("tune failed");
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
/* tune failed; put all the pid filtering cmd to junk */
state->pid_ctrl_index = -1;
return 0;
@@ -2137,7 +2124,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe)
/* turn off the diversity for the last frontend */
dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0);
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
if (state->pid_ctrl_index >= 0) {
u8 index_pid_filter_cmd;
u8 pid_ctrl_index = state->pid_ctrl_index;
@@ -2175,7 +2162,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
u8 index_frontend;
u16 lock = 0, lock_slave = 0;
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -2197,7 +2184,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
if ((lock & 0x0008) || (lock_slave & 0x0008))
*stat |= FE_HAS_LOCK;
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return 0;
}
@@ -2208,30 +2195,30 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
u16 *c;
int ret = 0;
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
- if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
dprintk("could not get the lock");
ret = -EINTR;
goto error;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR,
state->i2c_read_buffer, 16 * 2);
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
c = (u16 *)state->i2c_read_buffer;
*ber = c[10] << 16 | c[11];
error:
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return ret;
}
@@ -2243,7 +2230,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
u16 val;
int ret = 0;
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -2256,18 +2243,18 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
*strength += val;
}
- if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
dprintk("could not get the lock");
ret = -EINTR;
goto error;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
val = 65535 - c[4];
if (val > 65535 - *strength)
@@ -2276,7 +2263,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
*strength += val;
error:
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return ret;
}
@@ -2287,16 +2274,16 @@ static u32 dib9000_get_snr(struct dvb_frontend *fe)
u32 n, s, exp;
u16 val;
- if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
dprintk("could not get the lock");
return 0;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
return 0;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
val = c[7];
n = (val >> 4) & 0xff;
@@ -2326,7 +2313,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
u8 index_frontend;
u32 snr_master;
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
@@ -2340,7 +2327,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
} else
*snr = 0;
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return 0;
}
@@ -2351,27 +2338,27 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
u16 *c = (u16 *)state->i2c_read_buffer;
int ret = 0;
- if (DibAcquireLock(&state->demod_lock) < 0) {
+ if (mutex_lock_interruptible(&state->demod_lock) < 0) {
dprintk("could not get the lock");
return -EINTR;
}
- if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) {
dprintk("could not get the lock");
ret = -EINTR;
goto error;
}
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
- DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ mutex_unlock(&state->platform.risc.mem_mbx_lock);
*unc = c[12];
error:
- DibReleaseLock(&state->demod_lock);
+ mutex_unlock(&state->demod_lock);
return ret;
}
@@ -2514,11 +2501,11 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c
st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES;
st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS;
- DibInitLock(&st->platform.risc.mbx_if_lock);
- DibInitLock(&st->platform.risc.mbx_lock);
- DibInitLock(&st->platform.risc.mem_lock);
- DibInitLock(&st->platform.risc.mem_mbx_lock);
- DibInitLock(&st->demod_lock);
+ mutex_init(&st->platform.risc.mbx_if_lock);
+ mutex_init(&st->platform.risc.mbx_lock);
+ mutex_init(&st->platform.risc.mem_lock);
+ mutex_init(&st->platform.risc.mem_mbx_lock);
+ mutex_init(&st->demod_lock);
st->get_frontend_internal = 0;
st->pid_ctrl_index = -2;
diff --git a/drivers/media/dvb/frontends/drxd.h b/drivers/media/dvb/frontends/drxd.h
index 34398738f9b..216c8c3702f 100644
--- a/drivers/media/dvb/frontends/drxd.h
+++ b/drivers/media/dvb/frontends/drxd.h
@@ -51,9 +51,23 @@ struct drxd_config {
s16(*osc_deviation) (void *priv, s16 dev, int flag);
};
+#if defined(CONFIG_DVB_DRXD) || \
+ (defined(CONFIG_DVB_DRXD_MODULE) && defined(MODULE))
extern
struct dvb_frontend *drxd_attach(const struct drxd_config *config,
void *priv, struct i2c_adapter *i2c,
struct device *dev);
+#else
+static inline
+struct dvb_frontend *drxd_attach(const struct drxd_config *config,
+ void *priv, struct i2c_adapter *i2c,
+ struct device *dev)
+{
+ printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n",
+ __func__);
+ return NULL;
+}
+#endif
+
extern int drxd_config_i2c(struct dvb_frontend *, int);
#endif
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
index a414b1f2b6a..60b868faeac 100644
--- a/drivers/media/dvb/frontends/drxk_hard.c
+++ b/drivers/media/dvb/frontends/drxk_hard.c
@@ -1380,20 +1380,20 @@ static int DownloadMicrocode(struct drxk_state *state,
const u8 pMCImage[], u32 Length)
{
const u8 *pSrc = pMCImage;
- u16 Flags;
- u16 Drain;
u32 Address;
u16 nBlocks;
u16 BlockSize;
- u16 BlockCRC;
u32 offset = 0;
u32 i;
int status = 0;
dprintk(1, "\n");
- /* down the drain (we don care about MAGIC_WORD) */
+ /* down the drain (we don't care about MAGIC_WORD) */
+#if 0
+ /* For future reference */
Drain = (pSrc[0] << 8) | pSrc[1];
+#endif
pSrc += sizeof(u16);
offset += sizeof(u16);
nBlocks = (pSrc[0] << 8) | pSrc[1];
@@ -1410,11 +1410,17 @@ static int DownloadMicrocode(struct drxk_state *state,
pSrc += sizeof(u16);
offset += sizeof(u16);
+#if 0
+ /* For future reference */
Flags = (pSrc[0] << 8) | pSrc[1];
+#endif
pSrc += sizeof(u16);
offset += sizeof(u16);
+#if 0
+ /* For future reference */
BlockCRC = (pSrc[0] << 8) | pSrc[1];
+#endif
pSrc += sizeof(u16);
offset += sizeof(u16);
@@ -5829,7 +5835,7 @@ static int WriteGPIO(struct drxk_state *state)
}
if (state->UIO_mask & 0x0002) { /* UIO-2 */
/* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+ status = write16(state, SIO_PDR_SMA_RX_CFG__A, state->m_GPIOCfg);
if (status < 0)
goto error;
@@ -5848,7 +5854,7 @@ static int WriteGPIO(struct drxk_state *state)
}
if (state->UIO_mask & 0x0004) { /* UIO-3 */
/* write to io pad configuration register - output mode */
- status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg);
+ status = write16(state, SIO_PDR_GPIO_CFG__A, state->m_GPIOCfg);
if (status < 0)
goto error;
diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb/frontends/drxk_map.h
index 9b11a832886..23e16c12f23 100644
--- a/drivers/media/dvb/frontends/drxk_map.h
+++ b/drivers/media/dvb/frontends/drxk_map.h
@@ -432,6 +432,7 @@
#define SIO_PDR_UIO_OUT_LO__A 0x7F0016
#define SIO_PDR_OHW_CFG__A 0x7F001F
#define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3
+#define SIO_PDR_GPIO_CFG__A 0x7F0021
#define SIO_PDR_MSTRT_CFG__A 0x7F0025
#define SIO_PDR_MERR_CFG__A 0x7F0026
#define SIO_PDR_MCLK_CFG__A 0x7F0028
@@ -446,4 +447,5 @@
#define SIO_PDR_MD5_CFG__A 0x7F0030
#define SIO_PDR_MD6_CFG__A 0x7F0031
#define SIO_PDR_MD7_CFG__A 0x7F0032
+#define SIO_PDR_SMA_RX_CFG__A 0x7F0037
#define SIO_PDR_SMA_TX_CFG__A 0x7F0038
diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c
index af65d013db1..4c8ac2657c4 100644
--- a/drivers/media/dvb/frontends/ds3000.c
+++ b/drivers/media/dvb/frontends/ds3000.c
@@ -1114,7 +1114,10 @@ static int ds3000_set_frontend(struct dvb_frontend *fe)
ds3000_writereg(state,
ds3000_dvbs2_init_tab[i],
ds3000_dvbs2_init_tab[i + 1]);
- ds3000_writereg(state, 0xfe, 0x98);
+ if (c->symbol_rate >= 30000000)
+ ds3000_writereg(state, 0xfe, 0x54);
+ else
+ ds3000_writereg(state, 0xfe, 0x98);
break;
default:
return 1;
diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c
index 84df03c2917..708cbf19791 100644
--- a/drivers/media/dvb/frontends/it913x-fe.c
+++ b/drivers/media/dvb/frontends/it913x-fe.c
@@ -633,10 +633,9 @@ static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber)
{
struct it913x_fe_state *state = fe->demodulator_priv;
- int ret;
u8 reg[5];
/* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */
- ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
+ it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg));
state->ucblocks += (u32)(reg[1] << 8) | reg[0];
*ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2];
return 0;
@@ -658,10 +657,9 @@ static int it913x_fe_get_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct it913x_fe_state *state = fe->demodulator_priv;
- int ret;
u8 reg[8];
- ret = it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg));
+ it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg));
if (reg[3] < 3)
p->modulation = fe_con[reg[3]];
@@ -691,25 +689,25 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct it913x_fe_state *state = fe->demodulator_priv;
- int ret, i;
+ int i;
u8 empty_ch, last_ch;
state->it913x_status = 0;
/* Set bw*/
- ret = it913x_fe_select_bw(state, p->bandwidth_hz,
+ it913x_fe_select_bw(state, p->bandwidth_hz,
state->adcFrequency);
/* Training Mode Off */
- ret = it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0);
+ it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0);
/* Clear Empty Channel */
- ret = it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0);
+ it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0);
/* Clear bits */
- ret = it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0);
+ it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0);
/* LED on */
- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
+ it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1);
/* Select Band*/
if ((p->frequency >= 51000000) && (p->frequency <= 230000000))
i = 0;
@@ -720,7 +718,7 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe)
else
return -EOPNOTSUPP;
- ret = it913x_write_reg(state, PRO_DMOD, FREE_BAND, i);
+ it913x_write_reg(state, PRO_DMOD, FREE_BAND, i);
deb_info("Frontend Set Tuner Type %02x", state->tuner_type);
switch (state->tuner_type) {
@@ -730,7 +728,7 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe)
case IT9135_60:
case IT9135_61:
case IT9135_62:
- ret = it9137_set_tuner(state,
+ it9137_set_tuner(state,
p->bandwidth_hz, p->frequency);
break;
default:
@@ -742,9 +740,9 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe)
break;
}
/* LED off */
- ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
+ it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0);
/* Trigger ofsm */
- ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
+ it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0);
last_ch = 2;
for (i = 0; i < 40; ++i) {
empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS);
diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb/frontends/lg2160.c
new file mode 100644
index 00000000000..a3ab1a5b659
--- /dev/null
+++ b/drivers/media/dvb/frontends/lg2160.c
@@ -0,0 +1,1468 @@
+/*
+ * Support for LG2160 - ATSC/MH
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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/jiffies.h>
+#include <linux/dvb/frontend.h>
+#include "lg2160.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
+
+#define DBG_INFO 1
+#define DBG_REG 2
+
+#define lg_printk(kern, fmt, arg...) \
+ printk(kern "%s: " fmt, __func__, ##arg)
+
+#define lg_info(fmt, arg...) printk(KERN_INFO "lg2160: " fmt, ##arg)
+#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg)
+#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg)
+#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \
+ lg_printk(KERN_DEBUG, fmt, ##arg)
+#define lg_reg(fmt, arg...) if (debug & DBG_REG) \
+ lg_printk(KERN_DEBUG, fmt, ##arg)
+
+#define lg_fail(ret) \
+({ \
+ int __ret; \
+ __ret = (ret < 0); \
+ if (__ret) \
+ lg_err("error %d on line %d\n", ret, __LINE__); \
+ __ret; \
+})
+
+struct lg216x_state {
+ struct i2c_adapter *i2c_adap;
+ const struct lg2160_config *cfg;
+
+ struct dvb_frontend frontend;
+
+ u32 current_frequency;
+ u8 parade_id;
+ u8 fic_ver;
+ unsigned int last_reset;
+};
+
+/* ------------------------------------------------------------------------ */
+
+static int lg216x_write_reg(struct lg216x_state *state, u16 reg, u8 val)
+{
+ int ret;
+ u8 buf[] = { reg >> 8, reg & 0xff, val };
+ struct i2c_msg msg = {
+ .addr = state->cfg->i2c_addr, .flags = 0,
+ .buf = buf, .len = 3,
+ };
+
+ lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val);
+
+ ret = i2c_transfer(state->i2c_adap, &msg, 1);
+
+ if (ret != 1) {
+ lg_err("error (addr %02x %02x <- %02x, err = %i)\n",
+ msg.buf[0], msg.buf[1], msg.buf[2], ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+static int lg216x_read_reg(struct lg216x_state *state, u16 reg, u8 *val)
+{
+ int ret;
+ u8 reg_buf[] = { reg >> 8, reg & 0xff };
+ struct i2c_msg msg[] = {
+ { .addr = state->cfg->i2c_addr,
+ .flags = 0, .buf = reg_buf, .len = 2 },
+ { .addr = state->cfg->i2c_addr,
+ .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ lg_reg("reg: 0x%04x\n", reg);
+
+ ret = i2c_transfer(state->i2c_adap, msg, 2);
+
+ if (ret != 2) {
+ lg_err("error (addr %02x reg %04x error (ret == %i)\n",
+ state->cfg->i2c_addr, reg, ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+struct lg216x_reg {
+ u16 reg;
+ u8 val;
+};
+
+static int lg216x_write_regs(struct lg216x_state *state,
+ struct lg216x_reg *regs, int len)
+{
+ int i, ret;
+
+ lg_reg("writing %d registers...\n", len);
+
+ for (i = 0; i < len - 1; i++) {
+ ret = lg216x_write_reg(state, regs[i].reg, regs[i].val);
+ if (lg_fail(ret))
+ return ret;
+ }
+ return 0;
+}
+
+static int lg216x_set_reg_bit(struct lg216x_state *state,
+ u16 reg, int bit, int onoff)
+{
+ u8 val;
+ int ret;
+
+ lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff);
+
+ ret = lg216x_read_reg(state, reg, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= ~(1 << bit);
+ val |= (onoff & 1) << bit;
+
+ ret = lg216x_write_reg(state, reg, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg216x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct lg216x_state *state = fe->demodulator_priv;
+ int ret;
+
+ if (state->cfg->deny_i2c_rptr)
+ return 0;
+
+ lg_dbg("(%d)\n", enable);
+
+ ret = lg216x_set_reg_bit(state, 0x0000, 0, enable ? 0 : 1);
+
+ msleep(1);
+
+ return ret;
+}
+
+static int lg216x_soft_reset(struct lg216x_state *state)
+{
+ int ret;
+
+ lg_dbg("\n");
+
+ ret = lg216x_write_reg(state, 0x0002, 0x00);
+ if (lg_fail(ret))
+ goto fail;
+
+ msleep(20);
+ ret = lg216x_write_reg(state, 0x0002, 0x01);
+ if (lg_fail(ret))
+ goto fail;
+
+ state->last_reset = jiffies_to_msecs(jiffies);
+fail:
+ return ret;
+}
+
+static int lg216x_initialize(struct lg216x_state *state)
+{
+ int ret;
+
+ static struct lg216x_reg lg2160_init[] = {
+#if 0
+ { .reg = 0x0015, .val = 0xe6 },
+#else
+ { .reg = 0x0015, .val = 0xf7 },
+ { .reg = 0x001b, .val = 0x52 },
+ { .reg = 0x0208, .val = 0x00 },
+ { .reg = 0x0209, .val = 0x82 },
+ { .reg = 0x0210, .val = 0xf9 },
+ { .reg = 0x020a, .val = 0x00 },
+ { .reg = 0x020b, .val = 0x82 },
+ { .reg = 0x020d, .val = 0x28 },
+ { .reg = 0x020f, .val = 0x14 },
+#endif
+ };
+
+ static struct lg216x_reg lg2161_init[] = {
+ { .reg = 0x0000, .val = 0x41 },
+ { .reg = 0x0001, .val = 0xfb },
+ { .reg = 0x0216, .val = 0x00 },
+ { .reg = 0x0219, .val = 0x00 },
+ { .reg = 0x021b, .val = 0x55 },
+ { .reg = 0x0606, .val = 0x0a },
+ };
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_write_regs(state,
+ lg2160_init, ARRAY_SIZE(lg2160_init));
+ break;
+ case LG2161:
+ ret = lg216x_write_regs(state,
+ lg2161_init, ARRAY_SIZE(lg2161_init));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_soft_reset(state);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg216x_set_if(struct lg216x_state *state)
+{
+ u8 val;
+ int ret;
+
+ lg_dbg("%d KHz\n", state->cfg->if_khz);
+
+ ret = lg216x_read_reg(state, 0x0132, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xfb;
+ val |= (0 == state->cfg->if_khz) ? 0x04 : 0x00;
+
+ ret = lg216x_write_reg(state, 0x0132, val);
+ lg_fail(ret);
+
+ /* if NOT zero IF, 6 MHz is the default */
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg2160_agc_fix(struct lg216x_state *state,
+ int if_agc_fix, int rf_agc_fix)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0100, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xf3;
+ val |= (if_agc_fix) ? 0x08 : 0x00;
+ val |= (rf_agc_fix) ? 0x04 : 0x00;
+
+ ret = lg216x_write_reg(state, 0x0100, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+#if 0
+static int lg2160_agc_freeze(struct lg216x_state *state,
+ int if_agc_freeze, int rf_agc_freeze)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0100, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xcf;
+ val |= (if_agc_freeze) ? 0x20 : 0x00;
+ val |= (rf_agc_freeze) ? 0x10 : 0x00;
+
+ ret = lg216x_write_reg(state, 0x0100, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+#endif
+
+static int lg2160_agc_polarity(struct lg216x_state *state,
+ int if_agc_polarity, int rf_agc_polarity)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0100, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xfc;
+ val |= (if_agc_polarity) ? 0x02 : 0x00;
+ val |= (rf_agc_polarity) ? 0x01 : 0x00;
+
+ ret = lg216x_write_reg(state, 0x0100, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+static int lg2160_tuner_pwr_save_polarity(struct lg216x_state *state,
+ int polarity)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0008, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xfe;
+ val |= (polarity) ? 0x01 : 0x00;
+
+ ret = lg216x_write_reg(state, 0x0008, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+static int lg2160_spectrum_polarity(struct lg216x_state *state,
+ int inverted)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0132, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xfd;
+ val |= (inverted) ? 0x02 : 0x00;
+
+ ret = lg216x_write_reg(state, 0x0132, val);
+ lg_fail(ret);
+fail:
+ return lg216x_soft_reset(state);
+}
+
+static int lg2160_tuner_pwr_save(struct lg216x_state *state, int onoff)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0007, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xbf;
+ val |= (onoff) ? 0x40 : 0x00;
+
+ ret = lg216x_write_reg(state, 0x0007, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+static int lg216x_set_parade(struct lg216x_state *state, int id)
+{
+ int ret;
+
+ ret = lg216x_write_reg(state, 0x013e, id & 0x7f);
+ if (lg_fail(ret))
+ goto fail;
+
+ state->parade_id = id & 0x7f;
+fail:
+ return ret;
+}
+
+static int lg216x_set_ensemble(struct lg216x_state *state, int id)
+{
+ int ret;
+ u16 reg;
+ u8 val;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ reg = 0x0400;
+ break;
+ case LG2161:
+ default:
+ reg = 0x0500;
+ break;
+ }
+
+ ret = lg216x_read_reg(state, reg, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xfe;
+ val |= (id) ? 0x01 : 0x00;
+
+ ret = lg216x_write_reg(state, reg, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+static int lg2160_set_spi_clock(struct lg216x_state *state)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0014, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0xf3;
+ val |= (state->cfg->spi_clock << 2);
+
+ ret = lg216x_write_reg(state, 0x0014, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+static int lg2161_set_output_interface(struct lg216x_state *state)
+{
+ u8 val;
+ int ret;
+
+ ret = lg216x_read_reg(state, 0x0014, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= ~0x07;
+ val |= state->cfg->output_if; /* FIXME: needs sanity check */
+
+ ret = lg216x_write_reg(state, 0x0014, val);
+ lg_fail(ret);
+fail:
+ return ret;
+}
+
+static int lg216x_enable_fic(struct lg216x_state *state, int onoff)
+{
+ int ret;
+
+ ret = lg216x_write_reg(state, 0x0017, 0x23);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_write_reg(state, 0x0016, 0xfc);
+ if (lg_fail(ret))
+ goto fail;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_write_reg(state, 0x0016,
+ 0xfc | ((onoff) ? 0x02 : 0x00));
+ break;
+ case LG2161:
+ ret = lg216x_write_reg(state, 0x0016, (onoff) ? 0x10 : 0x00);
+ break;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_initialize(state);
+ if (lg_fail(ret))
+ goto fail;
+
+ if (onoff) {
+ ret = lg216x_write_reg(state, 0x0017, 0x03);
+ lg_fail(ret);
+ }
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg216x_get_fic_version(struct lg216x_state *state, u8 *ficver)
+{
+ u8 val;
+ int ret;
+
+ *ficver = 0xff; /* invalid value */
+
+ ret = lg216x_read_reg(state, 0x0128, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *ficver = (val >> 3) & 0x1f;
+fail:
+ return ret;
+}
+
+#if 0
+static int lg2160_get_parade_id(struct lg216x_state *state, u8 *id)
+{
+ u8 val;
+ int ret;
+
+ *id = 0xff; /* invalid value */
+
+ ret = lg216x_read_reg(state, 0x0123, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *id = val & 0x7f;
+fail:
+ return ret;
+}
+#endif
+
+static int lg216x_get_nog(struct lg216x_state *state, u8 *nog)
+{
+ u8 val;
+ int ret;
+
+ *nog = 0xff; /* invalid value */
+
+ ret = lg216x_read_reg(state, 0x0124, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *nog = ((val >> 4) & 0x07) + 1;
+fail:
+ return ret;
+}
+
+static int lg216x_get_tnog(struct lg216x_state *state, u8 *tnog)
+{
+ u8 val;
+ int ret;
+
+ *tnog = 0xff; /* invalid value */
+
+ ret = lg216x_read_reg(state, 0x0125, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *tnog = val & 0x1f;
+fail:
+ return ret;
+}
+
+static int lg216x_get_sgn(struct lg216x_state *state, u8 *sgn)
+{
+ u8 val;
+ int ret;
+
+ *sgn = 0xff; /* invalid value */
+
+ ret = lg216x_read_reg(state, 0x0124, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *sgn = val & 0x0f;
+fail:
+ return ret;
+}
+
+static int lg216x_get_prc(struct lg216x_state *state, u8 *prc)
+{
+ u8 val;
+ int ret;
+
+ *prc = 0xff; /* invalid value */
+
+ ret = lg216x_read_reg(state, 0x0125, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *prc = ((val >> 5) & 0x07) + 1;
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg216x_get_rs_frame_mode(struct lg216x_state *state,
+ enum atscmh_rs_frame_mode *rs_framemode)
+{
+ u8 val;
+ int ret;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_read_reg(state, 0x0410, &val);
+ break;
+ case LG2161:
+ ret = lg216x_read_reg(state, 0x0513, &val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ switch ((val >> 4) & 0x03) {
+#if 1
+ default:
+#endif
+ case 0x00:
+ *rs_framemode = ATSCMH_RSFRAME_PRI_ONLY;
+ break;
+ case 0x01:
+ *rs_framemode = ATSCMH_RSFRAME_PRI_SEC;
+ break;
+#if 0
+ default:
+ *rs_framemode = ATSCMH_RSFRAME_RES;
+ break;
+#endif
+ }
+fail:
+ return ret;
+}
+
+static
+int lg216x_get_rs_frame_ensemble(struct lg216x_state *state,
+ enum atscmh_rs_frame_ensemble *rs_frame_ens)
+{
+ u8 val;
+ int ret;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_read_reg(state, 0x0400, &val);
+ break;
+ case LG2161:
+ ret = lg216x_read_reg(state, 0x0500, &val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ val &= 0x01;
+ *rs_frame_ens = (enum atscmh_rs_frame_ensemble) val;
+fail:
+ return ret;
+}
+
+static int lg216x_get_rs_code_mode(struct lg216x_state *state,
+ enum atscmh_rs_code_mode *rs_code_pri,
+ enum atscmh_rs_code_mode *rs_code_sec)
+{
+ u8 val;
+ int ret;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_read_reg(state, 0x0410, &val);
+ break;
+ case LG2161:
+ ret = lg216x_read_reg(state, 0x0513, &val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ *rs_code_pri = (enum atscmh_rs_code_mode) ((val >> 2) & 0x03);
+ *rs_code_sec = (enum atscmh_rs_code_mode) (val & 0x03);
+fail:
+ return ret;
+}
+
+static int lg216x_get_sccc_block_mode(struct lg216x_state *state,
+ enum atscmh_sccc_block_mode *sccc_block)
+{
+ u8 val;
+ int ret;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_read_reg(state, 0x0315, &val);
+ break;
+ case LG2161:
+ ret = lg216x_read_reg(state, 0x0511, &val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ switch (val & 0x03) {
+ case 0x00:
+ *sccc_block = ATSCMH_SCCC_BLK_SEP;
+ break;
+ case 0x01:
+ *sccc_block = ATSCMH_SCCC_BLK_COMB;
+ break;
+ default:
+ *sccc_block = ATSCMH_SCCC_BLK_RES;
+ break;
+ }
+fail:
+ return ret;
+}
+
+static int lg216x_get_sccc_code_mode(struct lg216x_state *state,
+ enum atscmh_sccc_code_mode *mode_a,
+ enum atscmh_sccc_code_mode *mode_b,
+ enum atscmh_sccc_code_mode *mode_c,
+ enum atscmh_sccc_code_mode *mode_d)
+{
+ u8 val;
+ int ret;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_read_reg(state, 0x0316, &val);
+ break;
+ case LG2161:
+ ret = lg216x_read_reg(state, 0x0512, &val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ switch ((val >> 6) & 0x03) {
+ case 0x00:
+ *mode_a = ATSCMH_SCCC_CODE_HLF;
+ break;
+ case 0x01:
+ *mode_a = ATSCMH_SCCC_CODE_QTR;
+ break;
+ default:
+ *mode_a = ATSCMH_SCCC_CODE_RES;
+ break;
+ }
+
+ switch ((val >> 4) & 0x03) {
+ case 0x00:
+ *mode_b = ATSCMH_SCCC_CODE_HLF;
+ break;
+ case 0x01:
+ *mode_b = ATSCMH_SCCC_CODE_QTR;
+ break;
+ default:
+ *mode_b = ATSCMH_SCCC_CODE_RES;
+ break;
+ }
+
+ switch ((val >> 2) & 0x03) {
+ case 0x00:
+ *mode_c = ATSCMH_SCCC_CODE_HLF;
+ break;
+ case 0x01:
+ *mode_c = ATSCMH_SCCC_CODE_QTR;
+ break;
+ default:
+ *mode_c = ATSCMH_SCCC_CODE_RES;
+ break;
+ }
+
+ switch (val & 0x03) {
+ case 0x00:
+ *mode_d = ATSCMH_SCCC_CODE_HLF;
+ break;
+ case 0x01:
+ *mode_d = ATSCMH_SCCC_CODE_QTR;
+ break;
+ default:
+ *mode_d = ATSCMH_SCCC_CODE_RES;
+ break;
+ }
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+#if 0
+static int lg216x_read_fic_err_count(struct lg216x_state *state, u8 *err)
+{
+ u8 fic_err;
+ int ret;
+
+ *err = 0;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg216x_read_reg(state, 0x0012, &fic_err);
+ break;
+ case LG2161:
+ ret = lg216x_read_reg(state, 0x001e, &fic_err);
+ break;
+ }
+ if (lg_fail(ret))
+ goto fail;
+
+ *err = fic_err;
+fail:
+ return ret;
+}
+
+static int lg2160_read_crc_err_count(struct lg216x_state *state, u16 *err)
+{
+ u8 crc_err1, crc_err2;
+ int ret;
+
+ *err = 0;
+
+ ret = lg216x_read_reg(state, 0x0411, &crc_err1);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_read_reg(state, 0x0412, &crc_err2);
+ if (lg_fail(ret))
+ goto fail;
+
+ *err = (u16)(((crc_err2 & 0x0f) << 8) | crc_err1);
+fail:
+ return ret;
+}
+
+static int lg2161_read_crc_err_count(struct lg216x_state *state, u16 *err)
+{
+ u8 crc_err;
+ int ret;
+
+ *err = 0;
+
+ ret = lg216x_read_reg(state, 0x0612, &crc_err);
+ if (lg_fail(ret))
+ goto fail;
+
+ *err = (u16)crc_err;
+fail:
+ return ret;
+}
+
+static int lg216x_read_crc_err_count(struct lg216x_state *state, u16 *err)
+{
+ int ret;
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg2160_read_crc_err_count(state, err);
+ break;
+ case LG2161:
+ ret = lg2161_read_crc_err_count(state, err);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int lg2160_read_rs_err_count(struct lg216x_state *state, u16 *err)
+{
+ u8 rs_err1, rs_err2;
+ int ret;
+
+ *err = 0;
+
+ ret = lg216x_read_reg(state, 0x0413, &rs_err1);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_read_reg(state, 0x0414, &rs_err2);
+ if (lg_fail(ret))
+ goto fail;
+
+ *err = (u16)(((rs_err2 & 0x0f) << 8) | rs_err1);
+fail:
+ return ret;
+}
+
+static int lg2161_read_rs_err_count(struct lg216x_state *state, u16 *err)
+{
+ u8 rs_err1, rs_err2;
+ int ret;
+
+ *err = 0;
+
+ ret = lg216x_read_reg(state, 0x0613, &rs_err1);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_read_reg(state, 0x0614, &rs_err2);
+ if (lg_fail(ret))
+ goto fail;
+
+ *err = (u16)((rs_err1 << 8) | rs_err2);
+fail:
+ return ret;
+}
+
+static int lg216x_read_rs_err_count(struct lg216x_state *state, u16 *err)
+{
+ int ret;
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg2160_read_rs_err_count(state, err);
+ break;
+ case LG2161:
+ ret = lg2161_read_rs_err_count(state, err);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+#endif
+
+/* ------------------------------------------------------------------------ */
+
+static int lg216x_get_frontend(struct dvb_frontend *fe)
+{
+ struct lg216x_state *state = fe->demodulator_priv;
+ int ret;
+
+ lg_dbg("\n");
+
+ fe->dtv_property_cache.modulation = VSB_8;
+ fe->dtv_property_cache.frequency = state->current_frequency;
+ fe->dtv_property_cache.delivery_system = SYS_ATSCMH;
+
+ ret = lg216x_get_fic_version(state,
+ &fe->dtv_property_cache.atscmh_fic_ver);
+ if (lg_fail(ret))
+ goto fail;
+ if (state->fic_ver != fe->dtv_property_cache.atscmh_fic_ver) {
+ state->fic_ver = fe->dtv_property_cache.atscmh_fic_ver;
+
+#if 0
+ ret = lg2160_get_parade_id(state,
+ &fe->dtv_property_cache.atscmh_parade_id);
+ if (lg_fail(ret))
+ goto fail;
+/* #else */
+ fe->dtv_property_cache.atscmh_parade_id = state->parade_id;
+#endif
+ ret = lg216x_get_nog(state,
+ &fe->dtv_property_cache.atscmh_nog);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_get_tnog(state,
+ &fe->dtv_property_cache.atscmh_tnog);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_get_sgn(state,
+ &fe->dtv_property_cache.atscmh_sgn);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_get_prc(state,
+ &fe->dtv_property_cache.atscmh_prc);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_get_rs_frame_mode(state,
+ (enum atscmh_rs_frame_mode *)
+ &fe->dtv_property_cache.atscmh_rs_frame_mode);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_get_rs_frame_ensemble(state,
+ (enum atscmh_rs_frame_ensemble *)
+ &fe->dtv_property_cache.atscmh_rs_frame_ensemble);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_get_rs_code_mode(state,
+ (enum atscmh_rs_code_mode *)
+ &fe->dtv_property_cache.atscmh_rs_code_mode_pri,
+ (enum atscmh_rs_code_mode *)
+ &fe->dtv_property_cache.atscmh_rs_code_mode_sec);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_get_sccc_block_mode(state,
+ (enum atscmh_sccc_block_mode *)
+ &fe->dtv_property_cache.atscmh_sccc_block_mode);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_get_sccc_code_mode(state,
+ (enum atscmh_sccc_code_mode *)
+ &fe->dtv_property_cache.atscmh_sccc_code_mode_a,
+ (enum atscmh_sccc_code_mode *)
+ &fe->dtv_property_cache.atscmh_sccc_code_mode_b,
+ (enum atscmh_sccc_code_mode *)
+ &fe->dtv_property_cache.atscmh_sccc_code_mode_c,
+ (enum atscmh_sccc_code_mode *)
+ &fe->dtv_property_cache.atscmh_sccc_code_mode_d);
+ if (lg_fail(ret))
+ goto fail;
+ }
+#if 0
+ ret = lg216x_read_fic_err_count(state,
+ (u8 *)&fe->dtv_property_cache.atscmh_fic_err);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_read_crc_err_count(state,
+ &fe->dtv_property_cache.atscmh_crc_err);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_read_rs_err_count(state,
+ &fe->dtv_property_cache.atscmh_rs_err);
+ if (lg_fail(ret))
+ goto fail;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ if (((fe->dtv_property_cache.atscmh_rs_err >= 240) &&
+ (fe->dtv_property_cache.atscmh_crc_err >= 240)) &&
+ ((jiffies_to_msecs(jiffies) - state->last_reset) > 6000))
+ ret = lg216x_soft_reset(state);
+ break;
+ case LG2161:
+ /* no fix needed here (as far as we know) */
+ ret = 0;
+ break;
+ }
+ lg_fail(ret);
+#endif
+fail:
+ return ret;
+}
+
+static int lg216x_get_property(struct dvb_frontend *fe,
+ struct dtv_property *tvp)
+{
+ return (DTV_ATSCMH_FIC_VER == tvp->cmd) ?
+ lg216x_get_frontend(fe) : 0;
+}
+
+
+static int lg2160_set_frontend(struct dvb_frontend *fe)
+{
+ struct lg216x_state *state = fe->demodulator_priv;
+ int ret;
+
+ lg_dbg("(%d)\n", fe->dtv_property_cache.frequency);
+
+ if (fe->ops.tuner_ops.set_params) {
+ ret = fe->ops.tuner_ops.set_params(fe);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ if (lg_fail(ret))
+ goto fail;
+ state->current_frequency = fe->dtv_property_cache.frequency;
+ }
+
+ ret = lg2160_agc_fix(state, 0, 0);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg2160_agc_polarity(state, 0, 0);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg2160_tuner_pwr_save_polarity(state, 1);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg216x_set_if(state);
+ if (lg_fail(ret))
+ goto fail;
+ ret = lg2160_spectrum_polarity(state, state->cfg->spectral_inversion);
+ if (lg_fail(ret))
+ goto fail;
+
+ /* be tuned before this point */
+ ret = lg216x_soft_reset(state);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg2160_tuner_pwr_save(state, 0);
+ if (lg_fail(ret))
+ goto fail;
+
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg2160_set_spi_clock(state);
+ if (lg_fail(ret))
+ goto fail;
+ break;
+ case LG2161:
+ ret = lg2161_set_output_interface(state);
+ if (lg_fail(ret))
+ goto fail;
+ break;
+ }
+
+ ret = lg216x_set_parade(state, fe->dtv_property_cache.atscmh_parade_id);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_set_ensemble(state,
+ fe->dtv_property_cache.atscmh_rs_frame_ensemble);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_initialize(state);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_enable_fic(state, 1);
+ lg_fail(ret);
+
+ lg216x_get_frontend(fe);
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg2160_read_lock_status(struct lg216x_state *state,
+ int *acq_lock, int *sync_lock)
+{
+ u8 val;
+ int ret;
+
+ *acq_lock = 0;
+ *sync_lock = 0;
+
+ ret = lg216x_read_reg(state, 0x011b, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *sync_lock = (val & 0x20) ? 0 : 1;
+ *acq_lock = (val & 0x40) ? 0 : 1;
+fail:
+ return ret;
+}
+
+#ifdef USE_LG2161_LOCK_BITS
+static int lg2161_read_lock_status(struct lg216x_state *state,
+ int *acq_lock, int *sync_lock)
+{
+ u8 val;
+ int ret;
+
+ *acq_lock = 0;
+ *sync_lock = 0;
+
+ ret = lg216x_read_reg(state, 0x0304, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *sync_lock = (val & 0x80) ? 0 : 1;
+
+ ret = lg216x_read_reg(state, 0x011b, &val);
+ if (lg_fail(ret))
+ goto fail;
+
+ *acq_lock = (val & 0x40) ? 0 : 1;
+fail:
+ return ret;
+}
+#endif
+
+static int lg216x_read_lock_status(struct lg216x_state *state,
+ int *acq_lock, int *sync_lock)
+{
+#ifdef USE_LG2161_LOCK_BITS
+ int ret;
+ switch (state->cfg->lg_chip) {
+ case LG2160:
+ ret = lg2160_read_lock_status(state, acq_lock, sync_lock);
+ break;
+ case LG2161:
+ ret = lg2161_read_lock_status(state, acq_lock, sync_lock);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+#else
+ return lg2160_read_lock_status(state, acq_lock, sync_lock);
+#endif
+}
+
+static int lg216x_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct lg216x_state *state = fe->demodulator_priv;
+ int ret, acq_lock, sync_lock;
+
+ *status = 0;
+
+ ret = lg216x_read_lock_status(state, &acq_lock, &sync_lock);
+ if (lg_fail(ret))
+ goto fail;
+
+ lg_dbg("%s%s\n",
+ acq_lock ? "SIGNALEXIST " : "",
+ sync_lock ? "SYNCLOCK" : "");
+
+ if (acq_lock)
+ *status |= FE_HAS_SIGNAL;
+ if (sync_lock)
+ *status |= FE_HAS_SYNC;
+
+ if (*status)
+ *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK;
+
+fail:
+ return ret;
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg2160_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lg216x_state *state = fe->demodulator_priv;
+ u8 snr1, snr2;
+ int ret;
+
+ *snr = 0;
+
+ ret = lg216x_read_reg(state, 0x0202, &snr1);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_read_reg(state, 0x0203, &snr2);
+ if (lg_fail(ret))
+ goto fail;
+
+ if ((snr1 == 0xba) || (snr2 == 0xdf))
+ *snr = 0;
+ else
+#if 1
+ *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 >> 4);
+#else /* BCD */
+ *snr = (snr2 | (snr1 << 8));
+#endif
+fail:
+ return ret;
+}
+
+static int lg2161_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct lg216x_state *state = fe->demodulator_priv;
+ u8 snr1, snr2;
+ int ret;
+
+ *snr = 0;
+
+ ret = lg216x_read_reg(state, 0x0302, &snr1);
+ if (lg_fail(ret))
+ goto fail;
+
+ ret = lg216x_read_reg(state, 0x0303, &snr2);
+ if (lg_fail(ret))
+ goto fail;
+
+ if ((snr1 == 0xba) || (snr2 == 0xfd))
+ *snr = 0;
+ else
+
+ *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 & 0x0f);
+fail:
+ return ret;
+}
+
+static int lg216x_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+#if 0
+ /* borrowed from lgdt330x.c
+ *
+ * Calculate strength from SNR up to 35dB
+ * Even though the SNR can go higher than 35dB,
+ * there is some comfort factor in having a range of
+ * strong signals that can show at 100%
+ */
+ struct lg216x_state *state = fe->demodulator_priv;
+ u16 snr;
+ int ret;
+#endif
+ *strength = 0;
+#if 0
+ ret = fe->ops.read_snr(fe, &snr);
+ if (lg_fail(ret))
+ goto fail;
+ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
+ /* scale the range 0 - 35*2^24 into 0 - 65535 */
+ if (state->snr >= 8960 * 0x10000)
+ *strength = 0xffff;
+ else
+ *strength = state->snr / 8960;
+fail:
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+/* ------------------------------------------------------------------------ */
+
+static int lg216x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+#if 0
+ struct lg216x_state *state = fe->demodulator_priv;
+ int ret;
+
+ ret = lg216x_read_rs_err_count(state,
+ &fe->dtv_property_cache.atscmh_rs_err);
+ if (lg_fail(ret))
+ goto fail;
+
+ *ucblocks = fe->dtv_property_cache.atscmh_rs_err;
+fail:
+#else
+ *ucblocks = 0;
+#endif
+ return 0;
+}
+
+static int lg216x_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings
+ *fe_tune_settings)
+{
+ fe_tune_settings->min_delay_ms = 500;
+ lg_dbg("\n");
+ return 0;
+}
+
+static void lg216x_release(struct dvb_frontend *fe)
+{
+ struct lg216x_state *state = fe->demodulator_priv;
+ lg_dbg("\n");
+ kfree(state);
+}
+
+static struct dvb_frontend_ops lg2160_ops = {
+ .delsys = { SYS_ATSCMH },
+ .info = {
+ .name = "LG Electronics LG2160 ATSC/MH Frontend",
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
+ },
+ .i2c_gate_ctrl = lg216x_i2c_gate_ctrl,
+#if 0
+ .init = lg216x_init,
+ .sleep = lg216x_sleep,
+#endif
+ .get_property = lg216x_get_property,
+
+ .set_frontend = lg2160_set_frontend,
+ .get_frontend = lg216x_get_frontend,
+ .get_tune_settings = lg216x_get_tune_settings,
+ .read_status = lg216x_read_status,
+#if 0
+ .read_ber = lg216x_read_ber,
+#endif
+ .read_signal_strength = lg216x_read_signal_strength,
+ .read_snr = lg2160_read_snr,
+ .read_ucblocks = lg216x_read_ucblocks,
+ .release = lg216x_release,
+};
+
+static struct dvb_frontend_ops lg2161_ops = {
+ .delsys = { SYS_ATSCMH },
+ .info = {
+ .name = "LG Electronics LG2161 ATSC/MH Frontend",
+ .frequency_min = 54000000,
+ .frequency_max = 858000000,
+ .frequency_stepsize = 62500,
+ },
+ .i2c_gate_ctrl = lg216x_i2c_gate_ctrl,
+#if 0
+ .init = lg216x_init,
+ .sleep = lg216x_sleep,
+#endif
+ .get_property = lg216x_get_property,
+
+ .set_frontend = lg2160_set_frontend,
+ .get_frontend = lg216x_get_frontend,
+ .get_tune_settings = lg216x_get_tune_settings,
+ .read_status = lg216x_read_status,
+#if 0
+ .read_ber = lg216x_read_ber,
+#endif
+ .read_signal_strength = lg216x_read_signal_strength,
+ .read_snr = lg2161_read_snr,
+ .read_ucblocks = lg216x_read_ucblocks,
+ .release = lg216x_release,
+};
+
+struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
+ struct i2c_adapter *i2c_adap)
+{
+ struct lg216x_state *state = NULL;
+
+ lg_dbg("(%d-%04x)\n",
+ i2c_adap ? i2c_adapter_id(i2c_adap) : 0,
+ config ? config->i2c_addr : 0);
+
+ state = kzalloc(sizeof(struct lg216x_state), GFP_KERNEL);
+ if (state == NULL)
+ goto fail;
+
+ state->cfg = config;
+ state->i2c_adap = i2c_adap;
+ state->fic_ver = 0xff;
+ state->parade_id = 0xff;
+
+ switch (config->lg_chip) {
+ default:
+ lg_warn("invalid chip requested, defaulting to LG2160");
+ /* fall-thru */
+ case LG2160:
+ memcpy(&state->frontend.ops, &lg2160_ops,
+ sizeof(struct dvb_frontend_ops));
+ break;
+ case LG2161:
+ memcpy(&state->frontend.ops, &lg2161_ops,
+ sizeof(struct dvb_frontend_ops));
+ break;
+ }
+
+ state->frontend.demodulator_priv = state;
+ state->current_frequency = -1;
+ /* parade 1 by default */
+ state->frontend.dtv_property_cache.atscmh_parade_id = 1;
+
+ return &state->frontend;
+fail:
+ lg_warn("unable to detect LG216x hardware\n");
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(lg2160_attach);
+
+MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver");
+MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3");
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/dvb/frontends/lg2160.h b/drivers/media/dvb/frontends/lg2160.h
new file mode 100644
index 00000000000..9e2c0f41199
--- /dev/null
+++ b/drivers/media/dvb/frontends/lg2160.h
@@ -0,0 +1,84 @@
+/*
+ * Support for LG2160 - ATSC/MH
+ *
+ * Copyright (C) 2010 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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 _LG2160_H_
+#define _LG2160_H_
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+enum lg_chip_type {
+ LG2160 = 0,
+ LG2161 = 1,
+};
+
+#define LG2161_1019 LG2161
+#define LG2161_1040 LG2161
+
+enum lg2160_spi_clock {
+ LG2160_SPI_3_125_MHZ = 0,
+ LG2160_SPI_6_25_MHZ = 1,
+ LG2160_SPI_12_5_MHZ = 2,
+};
+
+#if 0
+enum lg2161_oif {
+ LG2161_OIF_EBI2_SLA = 1,
+ LG2161_OIF_SDIO_SLA = 2,
+ LG2161_OIF_SPI_SLA = 3,
+ LG2161_OIF_SPI_MAS = 4,
+ LG2161_OIF_SERIAL_TS = 7,
+};
+#endif
+
+struct lg2160_config {
+ u8 i2c_addr;
+
+ /* user defined IF frequency in KHz */
+ u16 if_khz;
+
+ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */
+ int deny_i2c_rptr:1;
+
+ /* spectral inversion - 0:disabled 1:enabled */
+ int spectral_inversion:1;
+
+ unsigned int output_if;
+ enum lg2160_spi_clock spi_clock;
+ enum lg_chip_type lg_chip;
+};
+
+#if defined(CONFIG_DVB_LG2160) || (defined(CONFIG_DVB_LG2160_MODULE) && \
+ defined(MODULE))
+extern
+struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
+ struct i2c_adapter *i2c_adap);
+#else
+static inline
+struct dvb_frontend *lg2160_attach(const struct lg2160_config *config,
+ struct i2c_adapter *i2c_adap)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_LG2160 */
+
+#endif /* _LG2160_H_ */
diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c
index 4de1d3520cd..568363a10a3 100644
--- a/drivers/media/dvb/frontends/lgs8gxx.c
+++ b/drivers/media/dvb/frontends/lgs8gxx.c
@@ -262,7 +262,6 @@ static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv)
static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
{
- int ret = 0;
u8 t;
if (priv->config->prod == LGS8GXX_PROD_LGS8G75) {
@@ -296,7 +295,7 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv)
if (priv->config->prod == LGS8GXX_PROD_LGS8913)
lgs8gxx_write_reg(priv, 0xC1, 0);
- ret = lgs8gxx_read_reg(priv, 0xC5, &t);
+ lgs8gxx_read_reg(priv, 0xC5, &t);
t = (t & 0xE0) | 0x06;
lgs8gxx_write_reg(priv, 0xC5, t);
diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c
index 045ee5a6f7a..312588e84da 100644
--- a/drivers/media/dvb/frontends/m88rs2000.c
+++ b/drivers/media/dvb/frontends/m88rs2000.c
@@ -416,9 +416,25 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state,
static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
{
- deb_info("%s: %s\n", __func__,
- volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
- volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 data;
+
+ data = m88rs2000_demod_read(state, 0xb2);
+ data |= 0x03; /* bit0 V/H, bit1 off/on */
+
+ switch (volt) {
+ case SEC_VOLTAGE_18:
+ data &= ~0x03;
+ break;
+ case SEC_VOLTAGE_13:
+ data &= ~0x03;
+ data |= 0x01;
+ break;
+ case SEC_VOLTAGE_OFF:
+ break;
+ }
+
+ m88rs2000_demod_write(state, 0xb2, data);
return 0;
}
@@ -654,7 +670,6 @@ static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset)
static int m88rs2000_set_fec(struct m88rs2000_state *state,
fe_code_rate_t fec)
{
- int ret;
u16 fec_set;
switch (fec) {
/* This is not confirmed kept for reference */
@@ -677,7 +692,7 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state,
default:
fec_set = 0x08;
}
- ret = m88rs2000_demod_write(state, 0x76, fec_set);
+ m88rs2000_demod_write(state, 0x76, fec_set);
return 0;
}
@@ -772,13 +787,13 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe)
return -ENODEV;
for (i = 0; i < 25; i++) {
- u8 reg = m88rs2000_demod_read(state, 0x8c);
+ reg = m88rs2000_demod_read(state, 0x8c);
if ((reg & 0x7) == 0x7) {
status = FE_HAS_LOCK;
break;
}
state->no_lock_count++;
- if (state->no_lock_count > 15) {
+ if (state->no_lock_count == 15) {
reg = m88rs2000_demod_read(state, 0x70);
reg ^= 0x4;
m88rs2000_demod_write(state, 0x70, reg);
diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c
index 45196c5b073..93612ebac51 100644
--- a/drivers/media/dvb/frontends/rtl2830.c
+++ b/drivers/media/dvb/frontends/rtl2830.c
@@ -374,6 +374,118 @@ err:
return ret;
}
+static int rtl2830_get_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret;
+ u8 buf[3];
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_regs(priv, 0x33c, buf, 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_rd_reg(priv, 0x351, &buf[2]);
+ if (ret)
+ goto err;
+
+ dbg("%s: TPS=%02x %02x %02x", __func__, buf[0], buf[1], buf[2]);
+
+ switch ((buf[0] >> 2) & 3) {
+ case 0:
+ c->modulation = QPSK;
+ break;
+ case 1:
+ c->modulation = QAM_16;
+ break;
+ case 2:
+ c->modulation = QAM_64;
+ break;
+ }
+
+ switch ((buf[2] >> 2) & 1) {
+ case 0:
+ c->transmission_mode = TRANSMISSION_MODE_2K;
+ break;
+ case 1:
+ c->transmission_mode = TRANSMISSION_MODE_8K;
+ }
+
+ switch ((buf[2] >> 0) & 3) {
+ case 0:
+ c->guard_interval = GUARD_INTERVAL_1_32;
+ break;
+ case 1:
+ c->guard_interval = GUARD_INTERVAL_1_16;
+ break;
+ case 2:
+ c->guard_interval = GUARD_INTERVAL_1_8;
+ break;
+ case 3:
+ c->guard_interval = GUARD_INTERVAL_1_4;
+ break;
+ }
+
+ switch ((buf[0] >> 4) & 7) {
+ case 0:
+ c->hierarchy = HIERARCHY_NONE;
+ break;
+ case 1:
+ c->hierarchy = HIERARCHY_1;
+ break;
+ case 2:
+ c->hierarchy = HIERARCHY_2;
+ break;
+ case 3:
+ c->hierarchy = HIERARCHY_4;
+ break;
+ }
+
+ switch ((buf[1] >> 3) & 7) {
+ case 0:
+ c->code_rate_HP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_HP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_HP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_HP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_HP = FEC_7_8;
+ break;
+ }
+
+ switch ((buf[1] >> 0) & 7) {
+ case 0:
+ c->code_rate_LP = FEC_1_2;
+ break;
+ case 1:
+ c->code_rate_LP = FEC_2_3;
+ break;
+ case 2:
+ c->code_rate_LP = FEC_3_4;
+ break;
+ case 3:
+ c->code_rate_LP = FEC_5_6;
+ break;
+ case 4:
+ c->code_rate_LP = FEC_7_8;
+ break;
+ }
+
+ return 0;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
{
struct rtl2830_priv *priv = fe->demodulator_priv;
@@ -404,14 +516,72 @@ err:
static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
{
- *snr = 0;
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret, hierarchy, constellation;
+ u8 buf[2], tmp;
+ u16 tmp16;
+#define CONSTELLATION_NUM 3
+#define HIERARCHY_NUM 4
+ static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = {
+ { 70705899, 70705899, 70705899, 70705899 },
+ { 82433173, 82433173, 87483115, 94445660 },
+ { 92888734, 92888734, 95487525, 99770748 },
+ };
+
+ if (priv->sleeping)
+ return 0;
+
+ /* reports SNR in resolution of 0.1 dB */
+
+ ret = rtl2830_rd_reg(priv, 0x33c, &tmp);
+ if (ret)
+ goto err;
+
+ constellation = (tmp >> 2) & 0x03; /* [3:2] */
+ if (constellation > CONSTELLATION_NUM - 1)
+ goto err;
+
+ hierarchy = (tmp >> 4) & 0x07; /* [6:4] */
+ if (hierarchy > HIERARCHY_NUM - 1)
+ goto err;
+
+ ret = rtl2830_rd_regs(priv, 0x40c, buf, 2);
+ if (ret)
+ goto err;
+
+ tmp16 = buf[0] << 8 | buf[1];
+
+ if (tmp16)
+ *snr = (snr_constant[constellation][hierarchy] -
+ intlog10(tmp16)) / ((1 << 24) / 100);
+ else
+ *snr = 0;
+
return 0;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
}
static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
{
- *ber = 0;
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_regs(priv, 0x34e, buf, 2);
+ if (ret)
+ goto err;
+
+ *ber = buf[0] << 8 | buf[1];
+
return 0;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
}
static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
@@ -422,8 +592,32 @@ static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
- *strength = 0;
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 buf[2];
+ u16 if_agc_raw, if_agc;
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_regs(priv, 0x359, buf, 2);
+ if (ret)
+ goto err;
+
+ if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff;
+
+ if (if_agc_raw & (1 << 9))
+ if_agc = -(~(if_agc_raw - 1) & 0x1ff);
+ else
+ if_agc = if_agc_raw;
+
+ *strength = (u8) (55 - if_agc / 182);
+ *strength |= *strength << 8;
+
return 0;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
}
static struct dvb_frontend_ops rtl2830_ops;
@@ -549,6 +743,7 @@ static struct dvb_frontend_ops rtl2830_ops = {
.get_tune_settings = rtl2830_get_tune_settings,
.set_frontend = rtl2830_set_frontend,
+ .get_frontend = rtl2830_get_frontend,
.read_status = rtl2830_read_status,
.read_snr = rtl2830_read_snr,
diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h
index 4a464761b5b..9b20557ccf6 100644
--- a/drivers/media/dvb/frontends/rtl2830_priv.h
+++ b/drivers/media/dvb/frontends/rtl2830_priv.h
@@ -22,6 +22,7 @@
#define RTL2830_PRIV_H
#include "dvb_frontend.h"
+#include "dvb_math.h"
#include "rtl2830.h"
#define LOG_PREFIX "rtl2830"
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
index dd08f4ac64a..8b0dc74a329 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -637,11 +637,9 @@ 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 */
@@ -823,15 +821,12 @@ static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t
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;
+ u8 f22_tx, 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);
@@ -849,7 +844,6 @@ static int stb0899_diseqc_init(struct stb0899_state *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;
}
diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c
index def88abb30b..2e93e65d2cd 100644
--- a/drivers/media/dvb/frontends/stb6100.c
+++ b/drivers/media/dvb/frontends/stb6100.c
@@ -158,7 +158,6 @@ static int stb6100_read_regs(struct stb6100_state *state, u8 regs[])
static int stb6100_read_reg(struct stb6100_state *state, u8 reg)
{
u8 regs[STB6100_NUMREGS];
- int rc;
struct i2c_msg msg = {
.addr = state->config->tuner_address + reg,
@@ -167,7 +166,7 @@ static int stb6100_read_reg(struct stb6100_state *state, u8 reg)
.len = 1
};
- rc = i2c_transfer(state->i2c, &msg, 1);
+ i2c_transfer(state->i2c, &msg, 1);
if (unlikely(reg >= STB6100_NUMREGS)) {
dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg);
diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c
index 85c157a1fe5..d40f226160e 100644
--- a/drivers/media/dvb/frontends/stv0297.c
+++ b/drivers/media/dvb/frontends/stv0297.c
@@ -414,7 +414,6 @@ static int stv0297_set_frontend(struct dvb_frontend *fe)
int delay;
int sweeprate;
int carrieroffset;
- unsigned long starttime;
unsigned long timeout;
fe_spectral_inversion_t inversion;
@@ -543,7 +542,6 @@ static int stv0297_set_frontend(struct dvb_frontend *fe)
stv0297_writereg_mask(state, 0x43, 0x10, 0x10);
/* wait for WGAGC lock */
- starttime = jiffies;
timeout = jiffies + msecs_to_jiffies(2000);
while (time_before(jiffies, timeout)) {
msleep(10);
diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c
index ba0709b2d43..4af20780fb9 100644
--- a/drivers/media/dvb/frontends/stv0900_sw.c
+++ b/drivers/media/dvb/frontends/stv0900_sw.c
@@ -835,7 +835,6 @@ static void stv0900_track_optimization(struct dvb_frontend *fe)
blind_tun_sw = 0,
modulation;
- enum fe_stv0900_rolloff rolloff;
enum fe_stv0900_modcode foundModcod;
dprintk("%s\n", __func__);
@@ -940,7 +939,6 @@ static void stv0900_track_optimization(struct dvb_frontend *fe)
freq1 = stv0900_read_reg(intp, CFR2);
freq0 = stv0900_read_reg(intp, CFR1);
- rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS);
if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) {
stv0900_write_reg(intp, SFRSTEP, 0x00);
stv0900_write_bits(intp, SCAN_ENABLE, 0);
diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c
index 4aef1877ed4..d79e69f65cb 100644
--- a/drivers/media/dvb/frontends/stv090x.c
+++ b/drivers/media/dvb/frontends/stv090x.c
@@ -2842,7 +2842,6 @@ static int stv090x_optimize_track(struct stv090x_state *state)
{
struct dvb_frontend *fe = &state->frontend;
- enum stv090x_rolloff rolloff;
enum stv090x_modcod modcod;
s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0;
@@ -2966,7 +2965,6 @@ static int stv090x_optimize_track(struct stv090x_state *state)
f_1 = STV090x_READ_DEMOD(state, CFR2);
f_0 = STV090x_READ_DEMOD(state, CFR1);
reg = STV090x_READ_DEMOD(state, TMGOBS);
- rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD);
if (state->algo == STV090x_BLIND_SEARCH) {
STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00);
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index ac723789137..82946cd517f 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -525,7 +525,7 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr)
zl10353_dump_regs(fe);
_snr = zl10353_read_register(state, SNR);
- *snr = (_snr << 8) | _snr;
+ *snr = 10 * _snr / 8;
return 0;
}
@@ -559,7 +559,6 @@ static int zl10353_init(struct dvb_frontend *fe)
{
struct zl10353_state *state = fe->demodulator_priv;
u8 zl10353_reset_attach[6] = { 0x50, 0x03, 0x64, 0x46, 0x15, 0x0F };
- int rc = 0;
if (debug_regs)
zl10353_dump_regs(fe);
@@ -573,7 +572,7 @@ static int zl10353_init(struct dvb_frontend *fe)
/* Do a "hard" reset if not already done */
if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] ||
zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) {
- rc = zl10353_write(fe, zl10353_reset_attach,
+ zl10353_write(fe, zl10353_reset_attach,
sizeof(zl10353_reset_attach));
if (debug_regs)
zl10353_dump_regs(fe);
diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/dvb/mantis/hopper_cards.c
index 71622f65c03..cc0251e0107 100644
--- a/drivers/media/dvb/mantis/hopper_cards.c
+++ b/drivers/media/dvb/mantis/hopper_cards.c
@@ -65,7 +65,7 @@ static int devs;
static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
{
- u32 stat = 0, mask = 0, lstat = 0;
+ u32 stat = 0, mask = 0;
u32 rst_stat = 0, rst_mask = 0;
struct mantis_pci *mantis;
@@ -80,7 +80,6 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id)
stat = mmread(MANTIS_INT_STAT);
mask = mmread(MANTIS_INT_MASK);
- lstat = stat & ~MANTIS_INT_RISCSTAT;
if (!(stat & mask))
return IRQ_NONE;
diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c
index c2bb90b3e52..095cf3a994e 100644
--- a/drivers/media/dvb/mantis/mantis_cards.c
+++ b/drivers/media/dvb/mantis/mantis_cards.c
@@ -73,7 +73,7 @@ static char *label[10] = {
static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
{
- u32 stat = 0, mask = 0, lstat = 0;
+ u32 stat = 0, mask = 0;
u32 rst_stat = 0, rst_mask = 0;
struct mantis_pci *mantis;
@@ -88,7 +88,6 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id)
stat = mmread(MANTIS_INT_STAT);
mask = mmread(MANTIS_INT_MASK);
- lstat = stat & ~MANTIS_INT_RISCSTAT;
if (!(stat & mask))
return IRQ_NONE;
diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/dvb/mantis/mantis_dma.c
index c61ca7d3dae..566c407175a 100644
--- a/drivers/media/dvb/mantis/mantis_dma.c
+++ b/drivers/media/dvb/mantis/mantis_dma.c
@@ -199,10 +199,6 @@ void mantis_dma_start(struct mantis_pci *mantis)
void mantis_dma_stop(struct mantis_pci *mantis)
{
- u32 stat = 0, mask = 0;
-
- stat = mmread(MANTIS_INT_STAT);
- mask = mmread(MANTIS_INT_MASK);
dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine");
mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR);
diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/dvb/mantis/mantis_evm.c
index 36f2256ebb0..71ce52875c3 100644
--- a/drivers/media/dvb/mantis/mantis_evm.c
+++ b/drivers/media/dvb/mantis/mantis_evm.c
@@ -41,10 +41,9 @@ static void mantis_hifevm_work(struct work_struct *work)
struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work);
struct mantis_pci *mantis = ca->ca_priv;
- u32 gpif_stat, gpif_mask;
+ u32 gpif_stat;
gpif_stat = mmread(MANTIS_GPIF_STATUS);
- gpif_mask = mmread(MANTIS_GPIF_IRQCFG);
if (gpif_stat & MANTIS_GPIF_DETSTAT) {
if (gpif_stat & MANTIS_CARD_PLUGIN) {
diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c
index f129a9303f8..39857384af1 100644
--- a/drivers/media/dvb/ngene/ngene-core.c
+++ b/drivers/media/dvb/ngene/ngene-core.c
@@ -1409,10 +1409,8 @@ static int ngene_start(struct ngene *dev)
if (stat < 0)
goto fail;
- if (!stat)
- return stat;
+ return 0;
- /* otherwise error: fall through */
fail:
ngwritel(0, NGENE_INT_ENABLE);
free_irq(dev->pci_dev->irq, dev);
diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c
index e1f20c23698..f148b19a206 100644
--- a/drivers/media/dvb/pluto2/pluto2.c
+++ b/drivers/media/dvb/pluto2/pluto2.c
@@ -481,14 +481,6 @@ static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe)
if (p->bandwidth_hz == 8000000)
buf[3] |= 0x08;
- if (sizeof(buf) == 6) {
- buf[4] = buf[2];
- buf[4] &= ~0x1c;
- buf[4] |= 0x18;
-
- buf[5] = (0 << 7) | (2 << 4);
- }
-
msg.addr = I2C_ADDR_TUA6034 >> 1;
msg.flags = 0;
msg.buf = buf;
diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
index 91f8c8291e2..d6f3f100699 100644
--- a/drivers/media/dvb/siano/smssdio.c
+++ b/drivers/media/dvb/siano/smssdio.c
@@ -114,7 +114,7 @@ out:
static void smssdio_interrupt(struct sdio_func *func)
{
- int ret, isr;
+ int ret;
struct smssdio_device *smsdev;
struct smscore_buffer_t *cb;
@@ -127,7 +127,7 @@ static void smssdio_interrupt(struct sdio_func *func)
* The interrupt register has no defined meaning. It is just
* a way of turning of the level triggered interrupt.
*/
- isr = sdio_readb(func, SMSSDIO_INT, &ret);
+ (void)sdio_readb(func, SMSSDIO_INT, &ret);
if (ret) {
sms_err("Unable to read interrupt register!\n");
return;
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
index b1fe5137df0..63c004a25e0 100644
--- a/drivers/media/dvb/siano/smsusb.c
+++ b/drivers/media/dvb/siano/smsusb.c
@@ -542,6 +542,8 @@ static const struct usb_device_id smsusb_id_table[] __devinitconst = {
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ USB_DEVICE(0x2040, 0xc090),
.driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+ { USB_DEVICE(0x2040, 0xc0a0),
+ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
{ } /* Terminating entry */
};
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index ee8ee1d481f..1b2d15140a1 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -107,7 +107,7 @@ static struct v4l2_input inputs[4] = {
.index = 1,
.name = "Television",
.type = V4L2_INPUT_TYPE_TUNER,
- .audioset = 2,
+ .audioset = 1,
.tuner = 0,
.std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M,
.status = 0,
@@ -494,7 +494,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
if (!av7110->analog_tuner_flags)
- return 0;
+ return input ? -EINVAL : 0;
if (input >= 4)
return -EINVAL;
@@ -503,19 +503,38 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
return av7110_dvb_c_switch(fh);
}
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+ if (a->index != 0)
+ return -EINVAL;
+ *a = msp3400_v4l2_audio;
+ return 0;
+}
+
static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
if (a->index != 0)
return -EINVAL;
- memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio));
+ if (av7110->current_input >= 2)
+ return -EINVAL;
+ *a = msp3400_v4l2_audio;
return 0;
}
static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
- return 0;
+ if (av7110->current_input >= 2)
+ return -EINVAL;
+ return a->index ? -EINVAL : 0;
}
static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh,
@@ -802,26 +821,39 @@ int av7110_init_v4l(struct av7110 *av7110)
ERR("cannot init capture device. skipping\n");
return -ENODEV;
}
- vv_data->ops.vidioc_enum_input = vidioc_enum_input;
- vv_data->ops.vidioc_g_input = vidioc_g_input;
- vv_data->ops.vidioc_s_input = vidioc_s_input;
- vv_data->ops.vidioc_g_tuner = vidioc_g_tuner;
- vv_data->ops.vidioc_s_tuner = vidioc_s_tuner;
- vv_data->ops.vidioc_g_frequency = vidioc_g_frequency;
- vv_data->ops.vidioc_s_frequency = vidioc_s_frequency;
- vv_data->ops.vidioc_g_audio = vidioc_g_audio;
- vv_data->ops.vidioc_s_audio = vidioc_s_audio;
- vv_data->ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
- vv_data->ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
- vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
+ vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data->vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data->vid_ops.vidioc_s_input = vidioc_s_input;
+ vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio;
+ vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio;
+ vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio;
+ vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL;
+
+ vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL;
+ vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
+ vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
+ vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
+
+ if (FW_VERSION(av7110->arm_app) < 0x2623)
+ vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT;
if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
ERR("cannot register capture device. skipping\n");
saa7146_vv_release(dev);
return -ENODEV;
}
- if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
- ERR("cannot register vbi v4l2 device. skipping\n");
+ if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+ if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI))
+ ERR("cannot register vbi v4l2 device. skipping\n");
+ }
return 0;
}
@@ -905,7 +937,7 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std)
static struct saa7146_ext_vv av7110_vv_data_st = {
.inputs = 1,
.audios = 1,
- .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT,
+ .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
.flags = 0,
.stds = &standard[0],
@@ -920,7 +952,7 @@ static struct saa7146_ext_vv av7110_vv_data_st = {
static struct saa7146_ext_vv av7110_vv_data_c = {
.inputs = 1,
.audios = 1,
- .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO,
.flags = SAA7146_USE_PORT_B_FOR_VBI,
.stds = &standard[0],
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 8b32e282bf5..12ddb53c58d 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -1483,9 +1483,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
ERR("cannot init vv subsystem\n");
return err;
}
- vv_data.ops.vidioc_enum_input = vidioc_enum_input;
- vv_data.ops.vidioc_g_input = vidioc_g_input;
- vv_data.ops.vidioc_s_input = vidioc_s_input;
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
/* fixme: proper cleanup here */
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index 056138f63c7..e1cd1328340 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -214,23 +214,76 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next);
* pipeline pointer must be identical for all nested calls to
* media_entity_pipeline_start().
*/
-void media_entity_pipeline_start(struct media_entity *entity,
- struct media_pipeline *pipe)
+__must_check int media_entity_pipeline_start(struct media_entity *entity,
+ struct media_pipeline *pipe)
{
struct media_device *mdev = entity->parent;
struct media_entity_graph graph;
+ struct media_entity *entity_err = entity;
+ int ret;
mutex_lock(&mdev->graph_mutex);
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
+ unsigned int i;
+
entity->stream_count++;
WARN_ON(entity->pipe && entity->pipe != pipe);
entity->pipe = pipe;
+
+ /* Already streaming --- no need to check. */
+ if (entity->stream_count > 1)
+ continue;
+
+ if (!entity->ops || !entity->ops->link_validate)
+ continue;
+
+ for (i = 0; i < entity->num_links; i++) {
+ struct media_link *link = &entity->links[i];
+
+ /* Is this pad part of an enabled link? */
+ if (!(link->flags & MEDIA_LNK_FL_ENABLED))
+ continue;
+
+ /* Are we the sink or not? */
+ if (link->sink->entity != entity)
+ continue;
+
+ ret = entity->ops->link_validate(link);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto error;
+ }
}
mutex_unlock(&mdev->graph_mutex);
+
+ return 0;
+
+error:
+ /*
+ * Link validation on graph failed. We revert what we did and
+ * return the error.
+ */
+ media_entity_graph_walk_start(&graph, entity_err);
+
+ while ((entity_err = media_entity_graph_walk_next(&graph))) {
+ entity_err->stream_count--;
+ if (entity_err->stream_count == 0)
+ entity_err->pipe = NULL;
+
+ /*
+ * We haven't increased stream_count further than this
+ * so we quit here.
+ */
+ if (entity_err == entity)
+ break;
+ }
+
+ mutex_unlock(&mdev->graph_mutex);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(media_entity_pipeline_start);
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 8db2d7f4b52..c257da13d76 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -320,7 +320,7 @@ config RADIO_MIROPCM20
module will be called radio-miropcm20.
config RADIO_SF16FMI
- tristate "SF16-FMI/SF16-FMP Radio"
+ tristate "SF16-FMI/SF16-FMP/SF16-FMD Radio"
depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards.
@@ -329,7 +329,7 @@ config RADIO_SF16FMI
module will be called radio-sf16fmi.
config RADIO_SF16FMR2
- tristate "SF16FMR2 Radio"
+ tristate "SF16-FMR2/SF16-FMD2 Radio"
depends on ISA && VIDEO_V4L2 && SND
---help---
Choose Y here if you have one of these FM radio cards.
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index f36905b6364..63b112b555b 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -1,92 +1,37 @@
/* 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.
-
- Major issue: I can't find out where the device reports the signal
- strength, and indeed the windows software appearantly just looks
- at the stereo indicator as well. So, scanning will only find
- stereo stations. Sad, but I can't help it.
-
- Also, the windows program sends oodles of messages over to the
- device, and I couldn't figure out their meaning. My suspicion
- is that they don't have any:-)
-
- You might find some interesting stuff about this module at
- http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
-
- Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- History:
-
- Version 0.46:
- Removed usb_dsbr100_open/close calls and radio->users counter. Also,
- radio->muted changed to radio->status and suspend/resume calls updated.
-
- Version 0.45:
- Converted to v4l2_device.
-
- 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
-
- Version 0.42:
- Converted dsbr100 to use video_ioctl2
- by Douglas Landgraf <dougsland@gmail.com>
-
- Version 0.41-ac1:
- Alan Cox: Some cleanups and fixes
-
- Version 0.41:
- Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
-
- Version 0.40:
- Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
-
- Version 0.30:
- Markus: Updates for 2.5.x kernel and more ISO compliant source
-
- Version 0.25:
- PSL and Markus: Cleanup, radio now doesn't stop on device close
-
- Version 0.24:
- Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
- right. Some minor cleanup, improved standalone compilation
-
- Version 0.23:
- Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
-
- Version 0.22:
- Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
- thanks to Mike Cox for pointing the problem out.
-
- Version 0.21:
- Markus: Minor cleanup, warnings if something goes wrong, lame attempt
- to adhere to Documentation/CodingStyle
-
- Version 0.2:
- Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
- Markus: Copyright clarification
-
- Version 0.01: Markus: initial release
-
+ * 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.
+ *
+ * Major issue: I can't find out where the device reports the signal
+ * strength, and indeed the windows software appearantly just looks
+ * at the stereo indicator as well. So, scanning will only find
+ * stereo stations. Sad, but I can't help it.
+ *
+ * Also, the windows program sends oodles of messages over to the
+ * device, and I couldn't figure out their meaning. My suspicion
+ * is that they don't have any:-)
+ *
+ * You might find some interesting stuff about this module at
+ * http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+ *
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
+ *
+ * Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
@@ -95,17 +40,19 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/videodev2.h>
+#include <linux/usb.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-#include <linux/usb.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
/*
* Version Information
*/
-#define DRIVER_VERSION "0.4.7"
-
-#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
-#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
+MODULE_AUTHOR("Markus Demleitner <msdemlei@tucana.harvard.edu>");
+MODULE_DESCRIPTION("D-Link DSB-R100 USB FM radio driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.1.0");
#define DSB100_VENDOR 0x04b4
#define DSB100_PRODUCT 0x1002
@@ -122,19 +69,8 @@ devices, that would be 76 and 91. */
#define FREQ_MAX 108.0
#define FREQ_MUL 16000
-/* defines for radio->status */
-#define STARTED 0
-#define STOPPED 1
-
#define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev)
-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_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);
@@ -143,179 +79,92 @@ struct dsbr100_device {
struct usb_device *usbdev;
struct video_device videodev;
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
u8 *transfer_buffer;
struct mutex v4l2_lock;
int curfreq;
- int stereo;
- int status;
-};
-
-static struct usb_device_id usb_dsbr100_device_table [] = {
- { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
- { } /* Terminating entry */
-};
-
-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,
- .suspend = usb_dsbr100_suspend,
- .resume = usb_dsbr100_resume,
- .reset_resume = usb_dsbr100_resume,
- .supports_autosuspend = 0,
+ bool stereo;
+ bool muted;
};
/* Low-level device interface begins here */
-/* switch on radio */
-static int dsbr100_start(struct dsbr100_device *radio)
+/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
+static int dsbr100_setfreq(struct dsbr100_device *radio, unsigned freq)
{
- int retval;
- int request;
-
- 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;
+ unsigned f = (freq / 16 * 80) / 1000 + 856;
+ int retval = 0;
+
+ if (!radio->muted) {
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ DSB100_TUNE,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ (f >> 8) & 0x00ff, f & 0xff,
+ radio->transfer_buffer, 8, 300);
+ if (retval >= 0)
+ mdelay(1);
}
- 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;
+ if (retval >= 0) {
+ radio->curfreq = freq;
+ return 0;
}
-
- radio->status = STARTED;
- return (radio->transfer_buffer)[0];
-
-usb_control_msg_failed:
dev_err(&radio->usbdev->dev,
"%s - usb_control_msg returned %i, request %i\n",
- __func__, retval, request);
+ __func__, retval, DSB100_TUNE);
return retval;
-
}
-/* switch off radio */
-static int dsbr100_stop(struct dsbr100_device *radio)
+/* switch on radio */
+static int dsbr100_start(struct dsbr100_device *radio)
{
- int retval;
- int request;
-
- 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,
+ int 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->status = STOPPED;
- return (radio->transfer_buffer)[0];
+ 0x01, 0x00, radio->transfer_buffer, 8, 300);
-usb_control_msg_failed:
+ if (retval >= 0)
+ return dsbr100_setfreq(radio, radio->curfreq);
dev_err(&radio->usbdev->dev,
"%s - usb_control_msg returned %i, request %i\n",
- __func__, retval, request);
+ __func__, retval, DSB100_ONOFF);
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)
+/* switch off radio */
+static int dsbr100_stop(struct dsbr100_device *radio)
{
- int retval;
- int request;
- int freq = (radio->curfreq / 16 * 80) / 1000 + 856;
-
- 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,
+ int retval = usb_control_msg(radio->usbdev,
usb_rcvctrlpipe(radio->usbdev, 0),
- USB_REQ_GET_STATUS,
+ DSB100_ONOFF,
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);
- return (radio->transfer_buffer)[0];
+ 0x00, 0x00, radio->transfer_buffer, 8, 300);
-usb_control_msg_failed:
- radio->stereo = -1;
+ if (retval >= 0)
+ return 0;
dev_err(&radio->usbdev->dev,
"%s - usb_control_msg returned %i, request %i\n",
- __func__, retval, request);
+ __func__, retval, DSB100_ONOFF);
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)
{
- int retval;
-
- retval = usb_control_msg(radio->usbdev,
+ int 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);
+ 0x00, 0x24, radio->transfer_buffer, 8, 300);
if (retval < 0) {
- radio->stereo = -1;
+ radio->stereo = false;
dev_err(&radio->usbdev->dev,
"%s - usb_control_msg returned %i, request %i\n",
__func__, retval, USB_REQ_GET_STATUS);
@@ -332,7 +181,8 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(v->driver, "dsbr100", sizeof(v->driver));
strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER;
+ v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -349,13 +199,11 @@ static int vidioc_g_tuner(struct file *file, void *priv,
v->type = V4L2_TUNER_RADIO;
v->rangelow = FREQ_MIN * FREQ_MUL;
v->rangehigh = FREQ_MAX * FREQ_MUL;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if(radio->stereo)
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff; /* We can't get the signal strength */
+ v->rxsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO :
+ V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ v->signal = radio->stereo ? 0xffff : 0; /* We can't get the signal strength */
return 0;
}
@@ -369,14 +217,12 @@ static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
struct dsbr100_device *radio = video_drvdata(file);
- int retval;
- radio->curfreq = f->frequency;
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
- retval = dsbr100_setfreq(radio);
- if (retval < 0)
- dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
- return 0;
+ return dsbr100_setfreq(radio, clamp_t(unsigned, f->frequency,
+ FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL));
}
static int vidioc_g_frequency(struct file *file, void *priv,
@@ -384,90 +230,26 @@ static int vidioc_g_frequency(struct file *file, void *priv,
{
struct dsbr100_device *radio = video_drvdata(file);
+ if (f->tuner)
+ return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
-
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int usb_dsbr100_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct dsbr100_device *radio = video_drvdata(file);
+ struct dsbr100_device *radio =
+ container_of(ctrl->handler, struct dsbr100_device, hdl);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- ctrl->value = radio->status;
- return 0;
+ radio->muted = ctrl->val;
+ return radio->muted ? dsbr100_stop(radio) : dsbr100_start(radio);
}
return -EINVAL;
}
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct dsbr100_device *radio = video_drvdata(file);
- int retval;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value) {
- retval = dsbr100_stop(radio);
- if (retval < 0) {
- dev_warn(&radio->usbdev->dev,
- "Radio did not respond properly\n");
- return -EBUSY;
- }
- } else {
- retval = dsbr100_start(radio);
- if (retval < 0) {
- dev_warn(&radio->usbdev->dev,
- "Radio did not respond properly\n");
- return -EBUSY;
- }
- }
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index > 1)
- return -EINVAL;
-
- strcpy(a->name, "Radio");
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
/* USB subsystem interface begins here */
@@ -481,8 +263,17 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
- v4l2_device_get(&radio->v4l2_dev);
mutex_lock(&radio->v4l2_lock);
+ /*
+ * Disconnect is also called on unload, and in that case we need to
+ * mute the device. This call will silently fail if it is called
+ * after a physical disconnect.
+ */
+ 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);
usb_set_intfdata(intf, NULL);
video_unregister_device(&radio->videodev);
v4l2_device_disconnect(&radio->v4l2_dev);
@@ -495,25 +286,13 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf)
static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
- int retval;
mutex_lock(&radio->v4l2_lock);
- if (radio->status == STARTED) {
- retval = dsbr100_stop(radio);
- if (retval < 0)
- dev_warn(&intf->dev, "dsbr100_stop failed\n");
-
- /* After dsbr100_stop() status set to STOPPED.
- * If we want driver to start radio on resume
- * we set status equal to STARTED.
- * On resume we will check status and run radio if needed.
- */
- radio->status = STARTED;
- }
+ if (!radio->muted && dsbr100_stop(radio) < 0)
+ dev_warn(&intf->dev, "dsbr100_stop failed\n");
mutex_unlock(&radio->v4l2_lock);
dev_info(&intf->dev, "going into suspend..\n");
-
return 0;
}
@@ -521,18 +300,13 @@ static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message)
static int usb_dsbr100_resume(struct usb_interface *intf)
{
struct dsbr100_device *radio = usb_get_intfdata(intf);
- int retval;
mutex_lock(&radio->v4l2_lock);
- if (radio->status == STARTED) {
- retval = dsbr100_start(radio);
- if (retval < 0)
- dev_warn(&intf->dev, "dsbr100_start failed\n");
- }
+ if (!radio->muted && dsbr100_start(radio) < 0)
+ dev_warn(&intf->dev, "dsbr100_start failed\n");
mutex_unlock(&radio->v4l2_lock);
dev_info(&intf->dev, "coming out of suspend..\n");
-
return 0;
}
@@ -541,15 +315,23 @@ static void usb_dsbr100_release(struct v4l2_device *v4l2_dev)
{
struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev);
+ v4l2_ctrl_handler_free(&radio->hdl);
v4l2_device_unregister(&radio->v4l2_dev);
kfree(radio->transfer_buffer);
kfree(radio);
}
+static const struct v4l2_ctrl_ops usb_dsbr100_ctrl_ops = {
+ .s_ctrl = usb_dsbr100_s_ctrl,
+};
+
/* File system interface */
static const struct v4l2_file_operations usb_dsbr100_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
};
static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
@@ -558,13 +340,9 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = {
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/* check if the device is present and register with v4l and usb if it is */
@@ -593,11 +371,17 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
retval = v4l2_device_register(&intf->dev, v4l2_dev);
if (retval < 0) {
v4l2_err(v4l2_dev, "couldn't register v4l2_device\n");
- kfree(radio->transfer_buffer);
- kfree(radio);
- return retval;
+ goto err_reg_dev;
}
+ v4l2_ctrl_handler_init(&radio->hdl, 1);
+ v4l2_ctrl_new_std(&radio->hdl, &usb_dsbr100_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ if (radio->hdl.error) {
+ retval = radio->hdl.error;
+ v4l2_err(v4l2_dev, "couldn't register control\n");
+ goto err_reg_ctrl;
+ }
mutex_init(&radio->v4l2_lock);
strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name));
radio->videodev.v4l2_dev = v4l2_dev;
@@ -605,28 +389,46 @@ static int usb_dsbr100_probe(struct usb_interface *intf,
radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops;
radio->videodev.release = video_device_release_empty;
radio->videodev.lock = &radio->v4l2_lock;
+ radio->videodev.ctrl_handler = &radio->hdl;
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags);
radio->usbdev = interface_to_usbdev(intf);
radio->curfreq = FREQ_MIN * FREQ_MUL;
- radio->status = STOPPED;
+ radio->muted = true;
video_set_drvdata(&radio->videodev, radio);
+ usb_set_intfdata(intf, radio);
retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr);
- if (retval < 0) {
- v4l2_err(v4l2_dev, "couldn't register video device\n");
- v4l2_device_unregister(v4l2_dev);
- kfree(radio->transfer_buffer);
- kfree(radio);
- return -EIO;
- }
- usb_set_intfdata(intf, radio);
- return 0;
+ if (retval == 0)
+ return 0;
+ v4l2_err(v4l2_dev, "couldn't register video device\n");
+
+err_reg_ctrl:
+ v4l2_ctrl_handler_free(&radio->hdl);
+ v4l2_device_unregister(v4l2_dev);
+err_reg_dev:
+ kfree(radio->transfer_buffer);
+ kfree(radio);
+ return retval;
}
-module_usb_driver(usb_dsbr100_driver);
+static struct usb_device_id usb_dsbr100_device_table[] = {
+ { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
+ { } /* Terminating entry */
+};
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
+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,
+ .suspend = usb_dsbr100_suspend,
+ .resume = usb_dsbr100_resume,
+ .reset_resume = usb_dsbr100_resume,
+};
+
+module_usb_driver(usb_dsbr100_driver);
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 2e639ce6f25..235c0e34982 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -29,6 +29,7 @@
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
+#include <linux/pnp.h>
#include <linux/slab.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
@@ -283,6 +284,16 @@ static const struct radio_isa_ops gemtek_ops = {
static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
+#ifdef CONFIG_PNP
+static struct pnp_device_id gemtek_pnp_devices[] = {
+ /* AOpen FX-3D/Pro Radio */
+ {.id = "ADS7183", .driver_data = 0},
+ {.id = ""}
+};
+
+MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices);
+#endif
+
static struct radio_isa_driver gemtek_driver = {
.driver = {
.match = radio_isa_match,
@@ -292,6 +303,14 @@ static struct radio_isa_driver gemtek_driver = {
.name = "radio-gemtek",
},
},
+#ifdef CONFIG_PNP
+ .pnp_driver = {
+ .name = "radio-gemtek",
+ .id_table = gemtek_pnp_devices,
+ .probe = radio_isa_pnp_probe,
+ .remove = radio_isa_pnp_remove,
+ },
+#endif
.io_params = io,
.radio_nr_params = radio_nr,
.io_ports = gemtek_ioports,
@@ -305,12 +324,18 @@ static struct radio_isa_driver gemtek_driver = {
static int __init gemtek_init(void)
{
gemtek_driver.probe = probe;
+#ifdef CONFIG_PNP
+ pnp_register_driver(&gemtek_driver.pnp_driver);
+#endif
return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
}
static void __exit gemtek_exit(void)
{
hardmute = 1; /* Turn off PLL */
+#ifdef CONFIG_PNP
+ pnp_unregister_driver(&gemtek_driver.pnp_driver);
+#endif
isa_unregister_driver(&gemtek_driver.driver);
}
diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c
index 06f906351fa..3c0067de432 100644
--- a/drivers/media/radio/radio-isa.c
+++ b/drivers/media/radio/radio-isa.c
@@ -150,14 +150,6 @@ static int radio_isa_log_status(struct file *file, void *priv)
return 0;
}
-static int radio_isa_subscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- if (sub->type == V4L2_EVENT_CTRL)
- return v4l2_event_subscribe(fh, sub, 0);
- return -EINVAL;
-}
-
static const struct v4l2_ctrl_ops radio_isa_ctrl_ops = {
.s_ctrl = radio_isa_s_ctrl,
};
@@ -177,7 +169,7 @@ static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = {
.vidioc_g_frequency = radio_isa_g_frequency,
.vidioc_s_frequency = radio_isa_s_frequency,
.vidioc_log_status = radio_isa_log_status,
- .vidioc_subscribe_event = radio_isa_subscribe_event,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
@@ -199,56 +191,31 @@ static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io)
return false;
}
-int radio_isa_probe(struct device *pdev, unsigned int dev)
+struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv,
+ struct device *pdev)
{
- struct radio_isa_driver *drv = pdev->platform_data;
- const struct radio_isa_ops *ops = drv->ops;
struct v4l2_device *v4l2_dev;
- struct radio_isa_card *isa;
- int res;
+ struct radio_isa_card *isa = drv->ops->alloc();
+ if (!isa)
+ return NULL;
- isa = drv->ops->alloc();
- if (isa == NULL)
- return -ENOMEM;
dev_set_drvdata(pdev, isa);
isa->drv = drv;
- isa->io = drv->io_params[dev];
v4l2_dev = &isa->v4l2_dev;
strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name));
- if (drv->probe && ops->probe) {
- int i;
-
- for (i = 0; i < drv->num_of_io_ports; ++i) {
- int io = drv->io_ports[i];
-
- if (request_region(io, drv->region_size, v4l2_dev->name)) {
- bool found = ops->probe(isa, io);
-
- release_region(io, drv->region_size);
- if (found) {
- isa->io = io;
- break;
- }
- }
- }
- }
-
- if (!radio_isa_valid_io(drv, isa->io)) {
- int i;
+ return isa;
+}
- if (isa->io < 0)
- return -ENODEV;
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x",
- drv->io_ports[0]);
- for (i = 1; i < drv->num_of_io_ports; i++)
- printk(KERN_CONT "/0x%03x", drv->io_ports[i]);
- printk(KERN_CONT ".\n");
- kfree(isa);
- return -EINVAL;
- }
+int radio_isa_common_probe(struct radio_isa_card *isa, struct device *pdev,
+ int radio_nr, unsigned region_size)
+{
+ const struct radio_isa_driver *drv = isa->drv;
+ const struct radio_isa_ops *ops = drv->ops;
+ struct v4l2_device *v4l2_dev = &isa->v4l2_dev;
+ int res;
- if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) {
+ if (!request_region(isa->io, region_size, v4l2_dev->name)) {
v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io);
kfree(isa);
return -EBUSY;
@@ -299,42 +266,126 @@ int radio_isa_probe(struct device *pdev, unsigned int dev)
res = ops->s_stereo(isa, isa->stereo);
if (res < 0) {
v4l2_err(v4l2_dev, "Could not setup card\n");
- goto err_node_reg;
+ goto err_hdl;
}
- res = video_register_device(&isa->vdev, VFL_TYPE_RADIO,
- drv->radio_nr_params[dev]);
+ res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, radio_nr);
+
if (res < 0) {
v4l2_err(v4l2_dev, "Could not register device node\n");
- goto err_node_reg;
+ goto err_hdl;
}
v4l2_info(v4l2_dev, "Initialized radio card %s on port 0x%03x\n",
drv->card, isa->io);
return 0;
-err_node_reg:
- v4l2_ctrl_handler_free(&isa->hdl);
err_hdl:
- v4l2_device_unregister(&isa->v4l2_dev);
+ v4l2_ctrl_handler_free(&isa->hdl);
err_dev_reg:
- release_region(isa->io, drv->region_size);
+ release_region(isa->io, region_size);
kfree(isa);
return res;
}
-EXPORT_SYMBOL_GPL(radio_isa_probe);
-int radio_isa_remove(struct device *pdev, unsigned int dev)
+int radio_isa_common_remove(struct radio_isa_card *isa, unsigned region_size)
{
- struct radio_isa_card *isa = dev_get_drvdata(pdev);
const struct radio_isa_ops *ops = isa->drv->ops;
ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0);
video_unregister_device(&isa->vdev);
v4l2_ctrl_handler_free(&isa->hdl);
v4l2_device_unregister(&isa->v4l2_dev);
- release_region(isa->io, isa->drv->region_size);
+ release_region(isa->io, region_size);
v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card);
kfree(isa);
return 0;
}
+
+int radio_isa_probe(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_driver *drv = pdev->platform_data;
+ const struct radio_isa_ops *ops = drv->ops;
+ struct v4l2_device *v4l2_dev;
+ struct radio_isa_card *isa;
+
+ isa = radio_isa_alloc(drv, pdev);
+ if (!isa)
+ return -ENOMEM;
+ isa->io = drv->io_params[dev];
+ v4l2_dev = &isa->v4l2_dev;
+
+ if (drv->probe && ops->probe) {
+ int i;
+
+ for (i = 0; i < drv->num_of_io_ports; ++i) {
+ int io = drv->io_ports[i];
+
+ if (request_region(io, drv->region_size, v4l2_dev->name)) {
+ bool found = ops->probe(isa, io);
+
+ release_region(io, drv->region_size);
+ if (found) {
+ isa->io = io;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!radio_isa_valid_io(drv, isa->io)) {
+ int i;
+
+ if (isa->io < 0)
+ return -ENODEV;
+ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x",
+ drv->io_ports[0]);
+ for (i = 1; i < drv->num_of_io_ports; i++)
+ printk(KERN_CONT "/0x%03x", drv->io_ports[i]);
+ printk(KERN_CONT ".\n");
+ kfree(isa);
+ return -EINVAL;
+ }
+
+ return radio_isa_common_probe(isa, pdev, drv->radio_nr_params[dev],
+ drv->region_size);
+}
+EXPORT_SYMBOL_GPL(radio_isa_probe);
+
+int radio_isa_remove(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_card *isa = dev_get_drvdata(pdev);
+
+ return radio_isa_common_remove(isa, isa->drv->region_size);
+}
EXPORT_SYMBOL_GPL(radio_isa_remove);
+
+#ifdef CONFIG_PNP
+int radio_isa_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+{
+ struct pnp_driver *pnp_drv = to_pnp_driver(dev->dev.driver);
+ struct radio_isa_driver *drv = container_of(pnp_drv,
+ struct radio_isa_driver, pnp_driver);
+ struct radio_isa_card *isa;
+
+ if (!pnp_port_valid(dev, 0))
+ return -ENODEV;
+
+ isa = radio_isa_alloc(drv, &dev->dev);
+ if (!isa)
+ return -ENOMEM;
+
+ isa->io = pnp_port_start(dev, 0);
+
+ return radio_isa_common_probe(isa, &dev->dev, drv->radio_nr_params[0],
+ pnp_port_len(dev, 0));
+}
+EXPORT_SYMBOL_GPL(radio_isa_pnp_probe);
+
+void radio_isa_pnp_remove(struct pnp_dev *dev)
+{
+ struct radio_isa_card *isa = dev_get_drvdata(&dev->dev);
+
+ radio_isa_common_remove(isa, pnp_port_len(dev, 0));
+}
+EXPORT_SYMBOL_GPL(radio_isa_pnp_remove);
+#endif
diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h
index 8a0ea84d86d..ba4c01f1bd0 100644
--- a/drivers/media/radio/radio-isa.h
+++ b/drivers/media/radio/radio-isa.h
@@ -24,6 +24,7 @@
#define _RADIO_ISA_H_
#include <linux/isa.h>
+#include <linux/pnp.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
@@ -76,6 +77,9 @@ struct radio_isa_ops {
/* Top level structure needed to instantiate the cards */
struct radio_isa_driver {
struct isa_driver driver;
+#ifdef CONFIG_PNP
+ struct pnp_driver pnp_driver;
+#endif
const struct radio_isa_ops *ops;
/* The module_param_array with the specified I/O ports */
int *io_params;
@@ -101,5 +105,10 @@ struct radio_isa_driver {
int radio_isa_match(struct device *pdev, unsigned int dev);
int radio_isa_probe(struct device *pdev, unsigned int dev);
int radio_isa_remove(struct device *pdev, unsigned int dev);
+#ifdef CONFIG_PNP
+int radio_isa_pnp_probe(struct pnp_dev *dev,
+ const struct pnp_device_id *dev_id);
+void radio_isa_pnp_remove(struct pnp_dev *dev);
+#endif
#endif
diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c
index 55bd1d2937c..79adf3e654e 100644
--- a/drivers/media/radio/radio-keene.c
+++ b/drivers/media/radio/radio-keene.c
@@ -28,7 +28,6 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-event.h>
#include <linux/usb.h>
-#include <linux/version.h>
#include <linux/mutex.h>
/* driver and module definitions */
@@ -149,7 +148,6 @@ static void usb_keene_disconnect(struct usb_interface *intf)
{
struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
- v4l2_device_get(&radio->v4l2_dev);
mutex_lock(&radio->lock);
usb_set_intfdata(intf, NULL);
video_unregister_device(&radio->vdev);
@@ -158,6 +156,23 @@ static void usb_keene_disconnect(struct usb_interface *intf)
v4l2_device_put(&radio->v4l2_dev);
}
+static int usb_keene_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
+
+ return keene_cmd_main(radio, 0, false);
+}
+
+static int usb_keene_resume(struct usb_interface *intf)
+{
+ struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
+
+ mdelay(50);
+ keene_cmd_set(radio);
+ keene_cmd_main(radio, radio->curfreq, true);
+ return 0;
+}
+
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
@@ -256,18 +271,6 @@ static int keene_s_ctrl(struct v4l2_ctrl *ctrl)
return -EINVAL;
}
-static int vidioc_subscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- switch (sub->type) {
- case V4L2_EVENT_CTRL:
- return v4l2_event_subscribe(fh, sub, 0);
- default:
- return -EINVAL;
- }
-}
-
-
/* File system interface */
static const struct v4l2_file_operations usb_keene_fops = {
.owner = THIS_MODULE,
@@ -288,7 +291,7 @@ static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = {
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
.vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
@@ -404,6 +407,9 @@ static struct usb_driver usb_keene_driver = {
.probe = usb_keene_probe,
.disconnect = usb_keene_disconnect,
.id_table = usb_keene_device_table,
+ .suspend = usb_keene_suspend,
+ .resume = usb_keene_resume,
+ .reset_resume = usb_keene_resume,
};
static int __init keene_init(void)
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
index a860a72a58e..94cb6bc690f 100644
--- a/drivers/media/radio/radio-mr800.c
+++ b/drivers/media/radio/radio-mr800.c
@@ -62,6 +62,8 @@
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <linux/usb.h>
#include <linux/mutex.h>
@@ -101,12 +103,17 @@ devices, that would be 76 and 91. */
* List isn't full and will be updated with implementation of new functions
*/
#define AMRADIO_SET_FREQ 0xa4
+#define AMRADIO_GET_READY_FLAG 0xa5
+#define AMRADIO_GET_SIGNAL 0xa7
+#define AMRADIO_GET_FREQ 0xa8
+#define AMRADIO_SET_SEARCH_UP 0xa9
+#define AMRADIO_SET_SEARCH_DOWN 0xaa
#define AMRADIO_SET_MUTE 0xab
+#define AMRADIO_SET_RIGHT_MUTE 0xac
+#define AMRADIO_SET_LEFT_MUTE 0xad
#define AMRADIO_SET_MONO 0xae
-
-/* Comfortable defines for amradio_set_mute */
-#define AMRADIO_START 0x00
-#define AMRADIO_STOP 0x01
+#define AMRADIO_SET_SEARCH_LVL 0xb0
+#define AMRADIO_STOP_SEARCH 0xb1
/* Comfortable defines for amradio_set_stereo */
#define WANT_STEREO 0x00
@@ -117,29 +124,20 @@ static int radio_nr = -1;
module_param(radio_nr, int, 0);
MODULE_PARM_DESC(radio_nr, "Radio Nr");
-static int usb_amradio_probe(struct usb_interface *intf,
- const struct usb_device_id *id);
-static void usb_amradio_disconnect(struct usb_interface *intf);
-static int usb_amradio_open(struct file *file);
-static int usb_amradio_close(struct file *file);
-static int usb_amradio_suspend(struct usb_interface *intf,
- pm_message_t message);
-static int usb_amradio_resume(struct usb_interface *intf);
-
/* Data for one (physical) device */
struct amradio_device {
/* reference to USB and video device */
struct usb_device *usbdev;
struct usb_interface *intf;
- struct video_device videodev;
+ struct video_device vdev;
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
- unsigned char *buffer;
+ u8 *buffer;
struct mutex lock; /* buffer locking */
int curfreq;
int stereo;
int muted;
- int initialized;
};
static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev)
@@ -147,29 +145,8 @@ static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev
return container_of(v4l2_dev, struct amradio_device, v4l2_dev);
}
-/* USB Device ID List */
-static struct usb_device_id usb_amradio_device_table[] = {
- {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
- USB_CLASS_HID, 0, 0) },
- { } /* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
-
-/* USB subsystem interface */
-static struct usb_driver usb_amradio_driver = {
- .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,
-};
-
-/* switch on/off the radio. Send 8 bytes to device */
-static int amradio_set_mute(struct amradio_device *radio, char argument)
+static int amradio_send_cmd(struct amradio_device *radio, u8 cmd, u8 arg,
+ u8 *extra, u8 extralen, bool reply)
{
int retval;
int size;
@@ -177,99 +154,92 @@ static int amradio_set_mute(struct amradio_device *radio, char argument)
radio->buffer[0] = 0x00;
radio->buffer[1] = 0x55;
radio->buffer[2] = 0xaa;
- radio->buffer[3] = 0x00;
- radio->buffer[4] = AMRADIO_SET_MUTE;
- radio->buffer[5] = argument;
+ radio->buffer[3] = extralen;
+ radio->buffer[4] = cmd;
+ radio->buffer[5] = arg;
radio->buffer[6] = 0x00;
- radio->buffer[7] = 0x00;
+ radio->buffer[7] = extra || reply ? 8 : 0;
retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
- (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+ radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT);
if (retval < 0 || size != BUFFER_LENGTH) {
- amradio_dev_warn(&radio->videodev.dev, "set mute failed\n");
- return retval;
+ if (video_is_registered(&radio->vdev))
+ amradio_dev_warn(&radio->vdev.dev,
+ "cmd %02x failed\n", cmd);
+ return retval ? retval : -EIO;
}
+ if (!extra && !reply)
+ return 0;
- radio->muted = argument;
+ if (extra) {
+ memcpy(radio->buffer, extra, extralen);
+ memset(radio->buffer + extralen, 0, 8 - extralen);
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT);
+ } else {
+ memset(radio->buffer, 0, 8);
+ retval = usb_bulk_msg(radio->usbdev, usb_rcvbulkpipe(radio->usbdev, 0x81),
+ radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT);
+ }
+ if (retval == 0 && size == BUFFER_LENGTH)
+ return 0;
+ if (video_is_registered(&radio->vdev) && cmd != AMRADIO_GET_READY_FLAG)
+ amradio_dev_warn(&radio->vdev.dev, "follow-up to cmd %02x failed\n", cmd);
+ return retval ? retval : -EIO;
+}
- return retval;
+/* switch on/off the radio. Send 8 bytes to device */
+static int amradio_set_mute(struct amradio_device *radio, bool mute)
+{
+ int ret = amradio_send_cmd(radio,
+ AMRADIO_SET_MUTE, mute, NULL, 0, false);
+
+ if (!ret)
+ radio->muted = mute;
+ return ret;
}
/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
-static int amradio_setfreq(struct amradio_device *radio, int freq)
+static int amradio_set_freq(struct amradio_device *radio, int freq)
{
- int retval;
- int size;
unsigned short freq_send = 0x10 + (freq >> 3) / 25;
-
- radio->buffer[0] = 0x00;
- radio->buffer[1] = 0x55;
- radio->buffer[2] = 0xaa;
- radio->buffer[3] = 0x03;
- radio->buffer[4] = AMRADIO_SET_FREQ;
- radio->buffer[5] = 0x00;
- radio->buffer[6] = 0x00;
- radio->buffer[7] = 0x08;
-
- retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
- (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
-
- if (retval < 0 || size != BUFFER_LENGTH)
- goto out_err;
+ u8 buf[3];
+ int retval;
/* frequency is calculated from freq_send and placed in first 2 bytes */
- radio->buffer[0] = (freq_send >> 8) & 0xff;
- radio->buffer[1] = freq_send & 0xff;
- radio->buffer[2] = 0x01;
- radio->buffer[3] = 0x00;
- radio->buffer[4] = 0x00;
- /* 5 and 6 bytes of buffer already = 0x00 */
- radio->buffer[7] = 0x00;
-
- retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
- (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
-
- if (retval < 0 || size != BUFFER_LENGTH)
- goto out_err;
+ buf[0] = (freq_send >> 8) & 0xff;
+ buf[1] = freq_send & 0xff;
+ buf[2] = 0x01;
+ retval = amradio_send_cmd(radio, AMRADIO_SET_FREQ, 0, buf, 3, false);
+ if (retval)
+ return retval;
radio->curfreq = freq;
- goto out;
-
-out_err:
- amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n");
-out:
- return retval;
+ msleep(40);
+ return 0;
}
-static int amradio_set_stereo(struct amradio_device *radio, char argument)
+static int amradio_set_stereo(struct amradio_device *radio, bool stereo)
{
- int retval;
- int size;
+ int ret = amradio_send_cmd(radio,
+ AMRADIO_SET_MONO, !stereo, NULL, 0, false);
- radio->buffer[0] = 0x00;
- radio->buffer[1] = 0x55;
- radio->buffer[2] = 0xaa;
- radio->buffer[3] = 0x00;
- radio->buffer[4] = AMRADIO_SET_MONO;
- radio->buffer[5] = argument;
- radio->buffer[6] = 0x00;
- radio->buffer[7] = 0x00;
-
- retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
- (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
-
- if (retval < 0 || size != BUFFER_LENGTH) {
- amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n");
- return retval;
- }
+ if (!ret)
+ radio->stereo = stereo;
+ return ret;
+}
- if (argument == WANT_STEREO)
- radio->stereo = 1;
- else
- radio->stereo = 0;
+static int amradio_get_stat(struct amradio_device *radio, bool *is_stereo, u32 *signal)
+{
+ int ret = amradio_send_cmd(radio,
+ AMRADIO_GET_SIGNAL, 0, NULL, 0, true);
- return retval;
+ if (ret)
+ return ret;
+ *is_stereo = radio->buffer[2] >> 7;
+ *signal = (radio->buffer[3] & 0xf0) << 8;
+ return 0;
}
/* Handle unplugging the device.
@@ -282,25 +252,26 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
mutex_lock(&radio->lock);
- /* increase the device node's refcount */
- get_device(&radio->videodev.dev);
+ video_unregister_device(&radio->vdev);
+ amradio_set_mute(radio, true);
+ usb_set_intfdata(intf, NULL);
v4l2_device_disconnect(&radio->v4l2_dev);
- video_unregister_device(&radio->videodev);
mutex_unlock(&radio->lock);
- /* decrease the device node's refcount, allowing it to be released */
- put_device(&radio->videodev.dev);
+ v4l2_device_put(&radio->v4l2_dev);
}
/* vidioc_querycap - query device capabilities */
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
- struct amradio_device *radio = file->private_data;
+ struct amradio_device *radio = video_drvdata(file);
strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER;
+ v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER |
+ V4L2_CAP_HW_FREQ_SEEK;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -308,44 +279,34 @@ static int vidioc_querycap(struct file *file, void *priv,
static int vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct amradio_device *radio = file->private_data;
+ struct amradio_device *radio = video_drvdata(file);
+ bool is_stereo = false;
int retval;
if (v->index > 0)
return -EINVAL;
-/* TODO: Add function which look is signal stereo or not
- * amradio_getstat(radio);
- */
-
-/* we call amradio_set_stereo to set radio->stereo
- * Honestly, amradio_getstat should cover this in future and
- * amradio_set_stereo shouldn't be here
- */
- retval = amradio_set_stereo(radio, WANT_STEREO);
+ v->signal = 0;
+ retval = amradio_get_stat(radio, &is_stereo, &v->signal);
+ if (retval)
+ return retval;
strcpy(v->name, "FM");
v->type = V4L2_TUNER_RADIO;
v->rangelow = FREQ_MIN * FREQ_MUL;
v->rangehigh = FREQ_MAX * FREQ_MUL;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (radio->stereo)
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff; /* Can't get the signal strength, sad.. */
- v->afc = 0; /* Don't know what is this */
-
- return retval;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ v->audmode = radio->stereo ?
+ V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
+ return 0;
}
/* vidioc_s_tuner - set tuner attributes */
static int vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *v)
{
- struct amradio_device *radio = file->private_data;
- int retval = -EINVAL;
+ struct amradio_device *radio = video_drvdata(file);
if (v->index > 0)
return -EINVAL;
@@ -353,34 +314,31 @@ static int vidioc_s_tuner(struct file *file, void *priv,
/* mono/stereo selector */
switch (v->audmode) {
case V4L2_TUNER_MODE_MONO:
- retval = amradio_set_stereo(radio, WANT_MONO);
- break;
- case V4L2_TUNER_MODE_STEREO:
- retval = amradio_set_stereo(radio, WANT_STEREO);
- break;
+ return amradio_set_stereo(radio, WANT_MONO);
+ default:
+ return amradio_set_stereo(radio, WANT_STEREO);
}
-
- return retval;
}
/* vidioc_s_frequency - set tuner radio frequency */
static int vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct amradio_device *radio = file->private_data;
+ struct amradio_device *radio = video_drvdata(file);
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ if (f->tuner != 0)
return -EINVAL;
- return amradio_setfreq(radio, f->frequency);
+ return amradio_set_freq(radio, clamp_t(unsigned, f->frequency,
+ FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL));
}
/* vidioc_g_frequency - get tuner radio frequency */
static int vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *f)
{
- struct amradio_device *radio = file->private_data;
+ struct amradio_device *radio = video_drvdata(file);
- if (f->tuner != 0)
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
return -EINVAL;
f->type = V4L2_TUNER_RADIO;
f->frequency = radio->curfreq;
@@ -388,148 +346,101 @@ static int vidioc_g_frequency(struct file *file, void *priv,
return 0;
}
-/* vidioc_queryctrl - enumerate control items */
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int vidioc_s_hw_freq_seek(struct file *file, void *priv,
+ struct v4l2_hw_freq_seek *seek)
{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
-
- return -EINVAL;
-}
+ static u8 buf[8] = {
+ 0x3d, 0x32, 0x0f, 0x08, 0x3d, 0x32, 0x0f, 0x08
+ };
+ struct amradio_device *radio = video_drvdata(file);
+ unsigned long timeout;
+ int retval;
-/* vidioc_g_ctrl - get the value of a control */
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct amradio_device *radio = file->private_data;
+ if (seek->tuner != 0 || !seek->wrap_around)
+ return -EINVAL;
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = radio->muted;
- return 0;
+ retval = amradio_send_cmd(radio,
+ AMRADIO_SET_SEARCH_LVL, 0, buf, 8, false);
+ if (retval)
+ return retval;
+ amradio_set_freq(radio, radio->curfreq);
+ retval = amradio_send_cmd(radio,
+ seek->seek_upward ? AMRADIO_SET_SEARCH_UP : AMRADIO_SET_SEARCH_DOWN,
+ 0, NULL, 0, false);
+ if (retval)
+ return retval;
+ timeout = jiffies + msecs_to_jiffies(30000);
+ for (;;) {
+ if (time_after(jiffies, timeout)) {
+ retval = -EAGAIN;
+ break;
+ }
+ if (schedule_timeout_interruptible(msecs_to_jiffies(10))) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ retval = amradio_send_cmd(radio, AMRADIO_GET_READY_FLAG,
+ 0, NULL, 0, true);
+ if (retval)
+ continue;
+ amradio_send_cmd(radio, AMRADIO_GET_FREQ, 0, NULL, 0, true);
+ if (radio->buffer[1] || radio->buffer[2]) {
+ radio->curfreq = (radio->buffer[1] << 8) | radio->buffer[2];
+ radio->curfreq = (radio->curfreq - 0x10) * 200;
+ amradio_send_cmd(radio, AMRADIO_STOP_SEARCH,
+ 0, NULL, 0, false);
+ amradio_set_freq(radio, radio->curfreq);
+ retval = 0;
+ break;
+ }
}
-
- return -EINVAL;
+ amradio_send_cmd(radio, AMRADIO_STOP_SEARCH, 0, NULL, 0, false);
+ amradio_set_freq(radio, radio->curfreq);
+ return retval;
}
-/* vidioc_s_ctrl - set the value of a control */
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int usb_amradio_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct amradio_device *radio = file->private_data;
- int retval = -EINVAL;
+ struct amradio_device *radio =
+ container_of(ctrl->handler, struct amradio_device, hdl);
switch (ctrl->id) {
case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- retval = amradio_set_mute(radio, AMRADIO_STOP);
- else
- retval = amradio_set_mute(radio, AMRADIO_START);
-
- break;
+ return amradio_set_mute(radio, ctrl->val);
}
- return retval;
-}
-
-/* vidioc_g_audio - get audio attributes */
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index > 1)
- return -EINVAL;
-
- strcpy(a->name, "Radio");
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-/* vidioc_s_audio - set audio attributes */
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- if (a->index != 0)
- return -EINVAL;
- return 0;
-}
-
-/* vidioc_g_input - get input */
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-/* vidioc_s_input - set input */
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- if (i != 0)
- return -EINVAL;
- return 0;
+ return -EINVAL;
}
static int usb_amradio_init(struct amradio_device *radio)
{
int retval;
- retval = amradio_set_mute(radio, AMRADIO_STOP);
+ retval = amradio_set_mute(radio, true);
if (retval)
goto out_err;
-
- retval = amradio_set_stereo(radio, WANT_STEREO);
+ retval = amradio_set_stereo(radio, true);
if (retval)
goto out_err;
-
- radio->initialized = 1;
- goto out;
-
-out_err:
- amradio_dev_err(&radio->videodev.dev, "initialization failed\n");
-out:
- return retval;
-}
-
-/* open device - amradio_start() and amradio_setfreq() */
-static int usb_amradio_open(struct file *file)
-{
- struct amradio_device *radio = video_drvdata(file);
- int retval;
-
- file->private_data = radio;
- retval = usb_autopm_get_interface(radio->intf);
+ retval = amradio_set_freq(radio, radio->curfreq);
if (retval)
- return retval;
+ goto out_err;
+ return 0;
- if (unlikely(!radio->initialized)) {
- retval = usb_amradio_init(radio);
- if (retval)
- usb_autopm_put_interface(radio->intf);
- }
+out_err:
+ amradio_dev_err(&radio->vdev.dev, "initialization failed\n");
return retval;
}
-/*close device */
-static int usb_amradio_close(struct file *file)
-{
- struct amradio_device *radio = file->private_data;
-
- if (video_is_registered(&radio->videodev))
- usb_autopm_put_interface(radio->intf);
- return 0;
-}
-
/* Suspend device - stop device. Need to be checked and fixed */
static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
{
struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
mutex_lock(&radio->lock);
- if (!radio->muted && radio->initialized) {
- amradio_set_mute(radio, AMRADIO_STOP);
- radio->muted = 0;
+ if (!radio->muted) {
+ amradio_set_mute(radio, true);
+ radio->muted = false;
}
mutex_unlock(&radio->lock);
@@ -543,31 +454,28 @@ static int usb_amradio_resume(struct usb_interface *intf)
struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf));
mutex_lock(&radio->lock);
- if (unlikely(!radio->initialized))
- goto unlock;
-
- if (radio->stereo)
- amradio_set_stereo(radio, WANT_STEREO);
- else
- amradio_set_stereo(radio, WANT_MONO);
-
- amradio_setfreq(radio, radio->curfreq);
+ amradio_set_stereo(radio, radio->stereo);
+ amradio_set_freq(radio, radio->curfreq);
if (!radio->muted)
- amradio_set_mute(radio, AMRADIO_START);
+ amradio_set_mute(radio, false);
-unlock:
mutex_unlock(&radio->lock);
dev_info(&intf->dev, "coming out of suspend..\n");
return 0;
}
+static const struct v4l2_ctrl_ops usb_amradio_ctrl_ops = {
+ .s_ctrl = usb_amradio_s_ctrl,
+};
+
/* File system interface */
static const struct v4l2_file_operations usb_amradio_fops = {
.owner = THIS_MODULE,
- .open = usb_amradio_open,
- .release = usb_amradio_close,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
};
@@ -577,20 +485,19 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = {
.vidioc_s_tuner = vidioc_s_tuner,
.vidioc_g_frequency = vidioc_g_frequency,
.vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+ .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
-static void usb_amradio_video_device_release(struct video_device *videodev)
+static void usb_amradio_release(struct v4l2_device *v4l2_dev)
{
- struct amradio_device *radio = video_get_drvdata(videodev);
+ struct amradio_device *radio = to_amradio_dev(v4l2_dev);
/* free rest memory */
+ v4l2_ctrl_handler_free(&radio->hdl);
+ v4l2_device_unregister(&radio->v4l2_dev);
kfree(radio->buffer);
kfree(radio);
}
@@ -624,23 +531,38 @@ static int usb_amradio_probe(struct usb_interface *intf,
goto err_v4l2;
}
+ v4l2_ctrl_handler_init(&radio->hdl, 1);
+ v4l2_ctrl_new_std(&radio->hdl, &usb_amradio_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ if (radio->hdl.error) {
+ retval = radio->hdl.error;
+ dev_err(&intf->dev, "couldn't register control\n");
+ goto err_ctrl;
+ }
mutex_init(&radio->lock);
- strlcpy(radio->videodev.name, radio->v4l2_dev.name,
- sizeof(radio->videodev.name));
- radio->videodev.v4l2_dev = &radio->v4l2_dev;
- radio->videodev.fops = &usb_amradio_fops;
- radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops;
- radio->videodev.release = usb_amradio_video_device_release;
- radio->videodev.lock = &radio->lock;
+ radio->v4l2_dev.ctrl_handler = &radio->hdl;
+ radio->v4l2_dev.release = usb_amradio_release;
+ strlcpy(radio->vdev.name, radio->v4l2_dev.name,
+ sizeof(radio->vdev.name));
+ radio->vdev.v4l2_dev = &radio->v4l2_dev;
+ radio->vdev.fops = &usb_amradio_fops;
+ radio->vdev.ioctl_ops = &usb_amradio_ioctl_ops;
+ radio->vdev.release = video_device_release_empty;
+ radio->vdev.lock = &radio->lock;
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
radio->usbdev = interface_to_usbdev(intf);
radio->intf = intf;
+ usb_set_intfdata(intf, &radio->v4l2_dev);
radio->curfreq = 95.16 * FREQ_MUL;
- video_set_drvdata(&radio->videodev, radio);
+ video_set_drvdata(&radio->vdev, radio);
+ retval = usb_amradio_init(radio);
+ if (retval)
+ goto err_vdev;
- retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
+ retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO,
radio_nr);
if (retval < 0) {
dev_err(&intf->dev, "could not register video device\n");
@@ -650,6 +572,8 @@ static int usb_amradio_probe(struct usb_interface *intf,
return 0;
err_vdev:
+ v4l2_ctrl_handler_free(&radio->hdl);
+err_ctrl:
v4l2_device_unregister(&radio->v4l2_dev);
err_v4l2:
kfree(radio->buffer);
@@ -659,4 +583,24 @@ err:
return retval;
}
+/* USB Device ID List */
+static struct usb_device_id usb_amradio_device_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT,
+ USB_CLASS_HID, 0, 0) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usb_amradio_device_table);
+
+/* USB subsystem interface */
+static struct usb_driver usb_amradio_driver = {
+ .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,
+};
+
module_usb_driver(usb_amradio_driver);
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index b275c5d0fe9..b1f844c64fd 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -17,6 +17,7 @@
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include "radio-isa.h"
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index 22c5743bf9d..a81d723b8c7 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -1,4 +1,4 @@
-/* SF16-FMI and SF16-FMP radio driver for Linux radio support
+/* SF16-FMI, SF16-FMP and SF16-FMD radio driver for Linux radio support
* heavily based on rtrack driver...
* (c) 1997 M. Kirkwood
* (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
@@ -11,7 +11,7 @@
*
* Frequency control is done digitally -- ie out(port,encodefreq(95.8));
* No volume control - only mute/unmute - you have to use line volume
- * control on SB-part of SF16-FMI/SF16-FMP
+ * control on SB-part of SF16-FMI/SF16-FMP/SF16-FMD
*
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
@@ -29,7 +29,7 @@
#include <media/v4l2-ioctl.h>
MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
-MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio.");
+MODULE_DESCRIPTION("A driver for the SF16-FMI, SF16-FMP and SF16-FMD radio.");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.3");
@@ -37,7 +37,7 @@ static int io = -1;
static int radio_nr = -1;
module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)");
+MODULE_PARM_DESC(io, "I/O address of the SF16-FMI/SF16-FMP/SF16-FMD card (0x284 or 0x384)");
module_param(radio_nr, int, 0);
struct fmi
@@ -130,7 +130,7 @@ static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *v)
{
strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
- strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
+ strlcpy(v->card, "SF16-FMI/FMP/FMD radio", sizeof(v->card));
strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
@@ -277,8 +277,12 @@ static const struct v4l2_ioctl_ops fmi_ioctl_ops = {
/* ladis: this is my card. does any other types exist? */
static struct isapnp_device_id id_table[] __devinitdata = {
+ /* SF16-FMI */
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID,
ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
+ /* SF16-FMD */
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+ ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad12), 0},
{ ISAPNP_CARD_END, },
};
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index 7c69214334b..52b8011f1b2 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -1,4 +1,4 @@
-/* SF16-FMR2 radio driver for Linux
+/* SF16-FMR2 and SF16-FMD2 radio driver for Linux
* Copyright (c) 2011 Ondrej Zary
*
* Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com
@@ -13,15 +13,19 @@
#include <linux/ioport.h> /* request_region */
#include <linux/io.h> /* outb, outb_p */
#include <linux/isa.h>
+#include <linux/pnp.h>
#include <sound/tea575x-tuner.h>
MODULE_AUTHOR("Ondrej Zary");
-MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
+MODULE_DESCRIPTION("MediaForte SF16-FMR2 and SF16-FMD2 FM radio card driver");
MODULE_LICENSE("GPL");
-static int radio_nr = -1;
-module_param(radio_nr, int, 0444);
-MODULE_PARM_DESC(radio_nr, "Radio device number");
+/* these cards can only use two different ports (0x384 and 0x284) */
+#define FMR2_MAX 2
+
+static int radio_nr[FMR2_MAX] = { [0 ... (FMR2_MAX - 1)] = -1 };
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
struct fmr2 {
int io;
@@ -29,9 +33,15 @@ struct fmr2 {
struct snd_tea575x tea;
struct v4l2_ctrl *volume;
struct v4l2_ctrl *balance;
+ bool is_fmd2;
};
-/* the port is hardwired so no need to support multiple cards */
+static int num_fmr2_cards;
+static struct fmr2 *fmr2_cards[FMR2_MAX];
+static bool isa_registered;
+static bool pnp_registered;
+
+/* the port is hardwired on SF16-FMR2 */
#define FMR2_PORT 0x384
/* TEA575x tuner pins */
@@ -174,7 +184,8 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea)
{
struct fmr2 *fmr2 = tea->private_data;
- if (inb(fmr2->io) & FMR2_HASVOL) {
+ /* FMR2 can have volume control, FMD2 can't (uses SB16 mixer) */
+ if (!fmr2->is_fmd2 && inb(fmr2->io) & FMR2_HASVOL) {
fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56);
fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0);
if (tea->ctrl_handler.error) {
@@ -186,22 +197,28 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea)
return 0;
}
-static int __devinit fmr2_probe(struct device *pdev, unsigned int dev)
+static struct pnp_device_id fmr2_pnp_ids[] __devinitdata = {
+ { .id = "MFRad13" }, /* tuner subdevice of SF16-FMD2 */
+ { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, fmr2_pnp_ids);
+
+static int __devinit fmr2_probe(struct fmr2 *fmr2, struct device *pdev, int io)
{
- struct fmr2 *fmr2;
- int err;
+ int err, i;
+ char *card_name = fmr2->is_fmd2 ? "SF16-FMD2" : "SF16-FMR2";
- fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL);
- if (fmr2 == NULL)
- return -ENOMEM;
+ /* avoid errors if a card was already registered at given port */
+ for (i = 0; i < num_fmr2_cards; i++)
+ if (io == fmr2_cards[i]->io)
+ return -EBUSY;
- strlcpy(fmr2->v4l2_dev.name, dev_name(pdev),
- sizeof(fmr2->v4l2_dev.name));
- fmr2->io = FMR2_PORT;
+ strlcpy(fmr2->v4l2_dev.name, "radio-sf16fmr2",
+ sizeof(fmr2->v4l2_dev.name)),
+ fmr2->io = io;
if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) {
printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io);
- kfree(fmr2);
return -EBUSY;
}
@@ -210,56 +227,121 @@ static int __devinit fmr2_probe(struct device *pdev, unsigned int dev)
if (err < 0) {
v4l2_err(&fmr2->v4l2_dev, "Could not register v4l2_device\n");
release_region(fmr2->io, 2);
- kfree(fmr2);
return err;
}
fmr2->tea.v4l2_dev = &fmr2->v4l2_dev;
fmr2->tea.private_data = fmr2;
- fmr2->tea.radio_nr = radio_nr;
+ fmr2->tea.radio_nr = radio_nr[num_fmr2_cards];
fmr2->tea.ops = &fmr2_tea_ops;
fmr2->tea.ext_init = fmr2_tea_ext_init;
- strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
- snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s",
- fmr2->v4l2_dev.name);
+ strlcpy(fmr2->tea.card, card_name, sizeof(fmr2->tea.card));
+ snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "%s:%s",
+ fmr2->is_fmd2 ? "PnP" : "ISA", dev_name(pdev));
if (snd_tea575x_init(&fmr2->tea)) {
printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n");
release_region(fmr2->io, 2);
- kfree(fmr2);
return -ENODEV;
}
- printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io);
+ printk(KERN_INFO "radio-sf16fmr2: %s radio card at 0x%x.\n",
+ card_name, fmr2->io);
return 0;
}
-static int __exit fmr2_remove(struct device *pdev, unsigned int dev)
+static int __devinit fmr2_isa_match(struct device *pdev, unsigned int ndev)
+{
+ struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL);
+ if (!fmr2)
+ return 0;
+
+ if (fmr2_probe(fmr2, pdev, FMR2_PORT)) {
+ kfree(fmr2);
+ return 0;
+ }
+ dev_set_drvdata(pdev, fmr2);
+ fmr2_cards[num_fmr2_cards++] = fmr2;
+
+ return 1;
+}
+
+static int __devinit fmr2_pnp_probe(struct pnp_dev *pdev,
+ const struct pnp_device_id *id)
{
- struct fmr2 *fmr2 = dev_get_drvdata(pdev);
+ int ret;
+ struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL);
+ if (!fmr2)
+ return -ENOMEM;
+ fmr2->is_fmd2 = true;
+ ret = fmr2_probe(fmr2, &pdev->dev, pnp_port_start(pdev, 0));
+ if (ret) {
+ kfree(fmr2);
+ return ret;
+ }
+ pnp_set_drvdata(pdev, fmr2);
+ fmr2_cards[num_fmr2_cards++] = fmr2;
+
+ return 0;
+}
+
+static void __devexit fmr2_remove(struct fmr2 *fmr2)
+{
snd_tea575x_exit(&fmr2->tea);
release_region(fmr2->io, 2);
v4l2_device_unregister(&fmr2->v4l2_dev);
kfree(fmr2);
+}
+
+static int __devexit fmr2_isa_remove(struct device *pdev, unsigned int ndev)
+{
+ fmr2_remove(dev_get_drvdata(pdev));
+ dev_set_drvdata(pdev, NULL);
+
return 0;
}
-struct isa_driver fmr2_driver = {
- .probe = fmr2_probe,
- .remove = fmr2_remove,
+static void __devexit fmr2_pnp_remove(struct pnp_dev *pdev)
+{
+ fmr2_remove(pnp_get_drvdata(pdev));
+ pnp_set_drvdata(pdev, NULL);
+}
+
+struct isa_driver fmr2_isa_driver = {
+ .match = fmr2_isa_match,
+ .remove = __devexit_p(fmr2_isa_remove),
.driver = {
.name = "radio-sf16fmr2",
},
};
+struct pnp_driver fmr2_pnp_driver = {
+ .name = "radio-sf16fmr2",
+ .id_table = fmr2_pnp_ids,
+ .probe = fmr2_pnp_probe,
+ .remove = __devexit_p(fmr2_pnp_remove),
+};
+
static int __init fmr2_init(void)
{
- return isa_register_driver(&fmr2_driver, 1);
+ int ret;
+
+ ret = pnp_register_driver(&fmr2_pnp_driver);
+ if (!ret)
+ pnp_registered = true;
+ ret = isa_register_driver(&fmr2_isa_driver, 1);
+ if (!ret)
+ isa_registered = true;
+
+ return (pnp_registered || isa_registered) ? 0 : ret;
}
static void __exit fmr2_exit(void)
{
- isa_unregister_driver(&fmr2_driver);
+ if (pnp_registered)
+ pnp_unregister_driver(&fmr2_pnp_driver);
+ if (isa_registered)
+ isa_unregister_driver(&fmr2_isa_driver);
}
module_init(fmr2_init);
diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c
index 5d9a90ac3a1..7052adc0c0b 100644
--- a/drivers/media/radio/radio-timb.c
+++ b/drivers/media/radio/radio-timb.c
@@ -223,7 +223,7 @@ static struct platform_driver timbradio_platform_driver = {
.owner = THIS_MODULE,
},
.probe = timbradio_probe,
- .remove = timbradio_remove,
+ .remove = __devexit_p(timbradio_remove),
};
module_platform_driver(timbradio_platform_driver);
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
index 9474706350f..bb953ef75f6 100644
--- a/drivers/media/radio/saa7706h.c
+++ b/drivers/media/radio/saa7706h.c
@@ -430,7 +430,7 @@ static struct i2c_driver saa7706h_driver = {
.name = DRIVER_NAME,
},
.probe = saa7706h_probe,
- .remove = saa7706h_remove,
+ .remove = __devexit_p(saa7706h_remove),
.id_table = saa7706h_id,
};
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 0e740c98786..969cf494d85 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -196,9 +196,9 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
}
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- dev_warn(&radio->videodev->dev, "tune does not complete\n");
+ dev_warn(&radio->videodev.dev, "tune does not complete\n");
if (timed_out)
- dev_warn(&radio->videodev->dev,
+ dev_warn(&radio->videodev.dev,
"tune timed out after %u ms\n", tune_timeout);
stop:
@@ -262,7 +262,7 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
*/
int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
{
- unsigned int spacing, band_bottom;
+ unsigned int spacing, band_bottom, band_top;
unsigned short chan;
/* Spacing (kHz) */
@@ -278,19 +278,26 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
spacing = 0.050 * FREQ_MUL; break;
};
- /* Bottom of Band (MHz) */
+ /* Bottom/Top of Band (MHz) */
switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
/* 0: 87.5 - 108 MHz (USA, Europe) */
case 0:
- band_bottom = 87.5 * FREQ_MUL; break;
+ band_bottom = 87.5 * FREQ_MUL;
+ band_top = 108 * FREQ_MUL;
+ break;
/* 1: 76 - 108 MHz (Japan wide band) */
default:
- band_bottom = 76 * FREQ_MUL; break;
+ band_bottom = 76 * FREQ_MUL;
+ band_top = 108 * FREQ_MUL;
+ break;
/* 2: 76 - 90 MHz (Japan) */
case 2:
- band_bottom = 76 * FREQ_MUL; break;
+ band_bottom = 76 * FREQ_MUL;
+ band_top = 90 * FREQ_MUL;
+ break;
};
+ freq = clamp(freq, band_bottom, band_top);
/* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
chan = (freq - band_bottom) / spacing;
@@ -320,7 +327,7 @@ static int si470x_set_seek(struct si470x_device *radio,
radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
retval = si470x_set_register(radio, POWERCFG);
if (retval < 0)
- goto done;
+ return retval;
/* currently I2C driver only uses interrupt way to seek */
if (radio->stci_enabled) {
@@ -344,24 +351,19 @@ static int si470x_set_seek(struct si470x_device *radio,
}
if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- dev_warn(&radio->videodev->dev, "seek does not complete\n");
+ dev_warn(&radio->videodev.dev, "seek does not complete\n");
if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
- dev_warn(&radio->videodev->dev,
+ dev_warn(&radio->videodev.dev,
"seek failed / band limit reached\n");
- if (timed_out)
- dev_warn(&radio->videodev->dev,
- "seek timed out after %u ms\n", seek_timeout);
stop:
/* stop seeking */
radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
retval = si470x_set_register(radio, POWERCFG);
-done:
/* try again, if timed out */
- if ((retval == 0) && timed_out)
- retval = -EAGAIN;
-
+ if (retval == 0 && timed_out)
+ return -EAGAIN;
return retval;
}
@@ -463,7 +465,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
unsigned int block_count = 0;
/* switch on rds reception */
- mutex_lock(&radio->lock);
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
si470x_rds_on(radio);
@@ -505,7 +506,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
}
done:
- mutex_unlock(&radio->lock);
return retval;
}
@@ -517,19 +517,19 @@ static unsigned int si470x_fops_poll(struct file *file,
struct poll_table_struct *pts)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
+ unsigned long req_events = poll_requested_events(pts);
+ int retval = v4l2_ctrl_poll(file, pts);
- /* switch on rds reception */
-
- mutex_lock(&radio->lock);
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
- si470x_rds_on(radio);
- mutex_unlock(&radio->lock);
+ if (req_events & (POLLIN | POLLRDNORM)) {
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ si470x_rds_on(radio);
- poll_wait(file, &radio->read_queue, pts);
+ poll_wait(file, &radio->read_queue, pts);
- if (radio->rd_index != radio->wr_index)
- retval = POLLIN | POLLRDNORM;
+ if (radio->rd_index != radio->wr_index)
+ retval |= POLLIN | POLLRDNORM;
+ }
return retval;
}
@@ -553,134 +553,26 @@ static const struct v4l2_file_operations si470x_fops = {
* Video4Linux Interface
**************************************************************************/
-/*
- * si470x_vidioc_queryctrl - enumerate control items
- */
-static int si470x_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = -EINVAL;
-
- /* abort if qc->id is below V4L2_CID_BASE */
- if (qc->id < V4L2_CID_BASE)
- goto done;
-
- /* search video control */
- switch (qc->id) {
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15);
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
-
- /* disable unsupported base controls */
- /* to satisfy kradio and such apps */
- if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) {
- qc->flags = V4L2_CTRL_FLAG_DISABLED;
- retval = 0;
- }
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "query controls failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_g_ctrl - get the value of a control
- */
-static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- mutex_lock(&radio->lock);
- /* safety checks */
- retval = si470x_disconnect_check(radio);
- if (retval)
- goto done;
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = radio->registers[SYSCONFIG2] &
- SYSCONFIG2_VOLUME;
- break;
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = ((radio->registers[POWERCFG] &
- POWERCFG_DMUTE) == 0) ? 1 : 0;
- break;
- default:
- retval = -EINVAL;
- }
-
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "get control failed with %d\n", retval);
-
- mutex_unlock(&radio->lock);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_ctrl - set the value of a control
- */
-static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int si470x_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- mutex_lock(&radio->lock);
- /* safety checks */
- retval = si470x_disconnect_check(radio);
- if (retval)
- goto done;
+ struct si470x_device *radio =
+ container_of(ctrl->handler, struct si470x_device, hdl);
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME;
- radio->registers[SYSCONFIG2] |= ctrl->value;
- retval = si470x_set_register(radio, SYSCONFIG2);
- break;
+ radio->registers[SYSCONFIG2] |= ctrl->val;
+ return si470x_set_register(radio, SYSCONFIG2);
case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value == 1)
+ if (ctrl->val)
radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
else
radio->registers[POWERCFG] |= POWERCFG_DMUTE;
- retval = si470x_set_register(radio, POWERCFG);
- break;
+ return si470x_set_register(radio, POWERCFG);
default:
- retval = -EINVAL;
+ return -EINVAL;
}
-
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "set control failed with %d\n", retval);
- mutex_unlock(&radio->lock);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_g_audio - get audio attributes
- */
-static int si470x_vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *audio)
-{
- /* driver constants */
- audio->index = 0;
- strcpy(audio->name, "Radio");
- audio->capability = V4L2_AUDCAP_STEREO;
- audio->mode = 0;
-
- return 0;
}
@@ -691,22 +583,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- mutex_lock(&radio->lock);
- /* safety checks */
- retval = si470x_disconnect_check(radio);
- if (retval)
- goto done;
+ int retval;
- if (tuner->index != 0) {
- retval = -EINVAL;
- goto done;
- }
+ if (tuner->index != 0)
+ return -EINVAL;
retval = si470x_get_register(radio, STATUSRSSI);
if (retval < 0)
- goto done;
+ return retval;
/* driver constants */
strcpy(tuner->name, "FM");
@@ -737,7 +621,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
else
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ tuner->rxsubchans = V4L2_TUNER_SUB_STEREO;
/* If there is a reliable method of detecting an RDS channel,
then this code should check for that before setting this
RDS subchannel. */
@@ -754,16 +638,13 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
/* the ideal factor is 0xffff/75 = 873,8 */
tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
+ if (tuner->signal > 0xffff)
+ tuner->signal = 0xffff;
/* automatic frequency control: -1: freq to low, 1 freq to high */
/* AFCRL does only indicate that freq. differs, not if too low/high */
tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "get tuner failed with %d\n", retval);
- mutex_unlock(&radio->lock);
return retval;
}
@@ -775,16 +656,9 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- mutex_lock(&radio->lock);
- /* safety checks */
- retval = si470x_disconnect_check(radio);
- if (retval)
- goto done;
if (tuner->index != 0)
- goto done;
+ return -EINVAL;
/* mono/stereo selector */
switch (tuner->audmode) {
@@ -792,20 +666,12 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
break;
case V4L2_TUNER_MODE_STEREO:
+ default:
radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
break;
- default:
- goto done;
}
- retval = si470x_set_register(radio, POWERCFG);
-
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "set tuner failed with %d\n", retval);
- mutex_unlock(&radio->lock);
- return retval;
+ return si470x_set_register(radio, POWERCFG);
}
@@ -816,28 +682,12 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- mutex_lock(&radio->lock);
- retval = si470x_disconnect_check(radio);
- if (retval)
- goto done;
- if (freq->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
+ if (freq->tuner != 0)
+ return -EINVAL;
freq->type = V4L2_TUNER_RADIO;
- retval = si470x_get_freq(radio, &freq->frequency);
-
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "get frequency failed with %d\n", retval);
- mutex_unlock(&radio->lock);
- return retval;
+ return si470x_get_freq(radio, &freq->frequency);
}
@@ -848,27 +698,11 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- mutex_lock(&radio->lock);
- /* safety checks */
- retval = si470x_disconnect_check(radio);
- if (retval)
- goto done;
- if (freq->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
+ if (freq->tuner != 0)
+ return -EINVAL;
- retval = si470x_set_freq(radio, freq->frequency);
-
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "set frequency failed with %d\n", retval);
- mutex_unlock(&radio->lock);
- return retval;
+ return si470x_set_freq(radio, freq->frequency);
}
@@ -879,44 +713,29 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
struct v4l2_hw_freq_seek *seek)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- mutex_lock(&radio->lock);
- /* safety checks */
- retval = si470x_disconnect_check(radio);
- if (retval)
- goto done;
-
- if (seek->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
- retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+ if (seek->tuner != 0)
+ return -EINVAL;
-done:
- if (retval < 0)
- dev_warn(&radio->videodev->dev,
- "set hardware frequency seek failed with %d\n", retval);
- mutex_unlock(&radio->lock);
- return retval;
+ return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
}
+const struct v4l2_ctrl_ops si470x_ctrl_ops = {
+ .s_ctrl = si470x_s_ctrl,
+};
/*
* si470x_ioctl_ops - video device ioctl operations
*/
static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
.vidioc_querycap = si470x_vidioc_querycap,
- .vidioc_queryctrl = si470x_vidioc_queryctrl,
- .vidioc_g_ctrl = si470x_vidioc_g_ctrl,
- .vidioc_s_ctrl = si470x_vidioc_s_ctrl,
- .vidioc_g_audio = si470x_vidioc_g_audio,
.vidioc_g_tuner = si470x_vidioc_g_tuner,
.vidioc_s_tuner = si470x_vidioc_s_tuner,
.vidioc_g_frequency = si470x_vidioc_g_frequency,
.vidioc_s_frequency = si470x_vidioc_s_frequency,
.vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
@@ -926,6 +745,6 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = {
struct video_device si470x_viddev_template = {
.fops = &si470x_fops,
.name = DRIVER_NAME,
- .release = video_device_release,
+ .release = video_device_release_empty,
.ioctl_ops = &si470x_ioctl_ops,
};
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index 9b546a5523f..a80044c5874 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -162,20 +162,6 @@ static int si470x_get_all_registers(struct si470x_device *radio)
/**************************************************************************
- * General Driver Functions - DISCONNECT_CHECK
- **************************************************************************/
-
-/*
- * si470x_disconnect_check - check whether radio disconnects
- */
-int si470x_disconnect_check(struct si470x_device *radio)
-{
- return 0;
-}
-
-
-
-/**************************************************************************
* File Operations Interface
**************************************************************************/
@@ -185,12 +171,12 @@ int si470x_disconnect_check(struct si470x_device *radio)
int si470x_fops_open(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
+ int retval = v4l2_fh_open(file);
- mutex_lock(&radio->lock);
- radio->users++;
+ if (retval)
+ return retval;
- if (radio->users == 1) {
+ if (v4l2_fh_is_singular_file(file)) {
/* start radio */
retval = si470x_start(radio);
if (retval < 0)
@@ -205,7 +191,8 @@ int si470x_fops_open(struct file *file)
}
done:
- mutex_unlock(&radio->lock);
+ if (retval)
+ v4l2_fh_release(file);
return retval;
}
@@ -216,21 +203,12 @@ done:
int si470x_fops_release(struct file *file)
{
struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety check */
- if (!radio)
- return -ENODEV;
- mutex_lock(&radio->lock);
- radio->users--;
- if (radio->users == 0)
+ if (v4l2_fh_is_singular_file(file))
/* stop radio */
- retval = si470x_stop(radio);
+ si470x_stop(radio);
- mutex_unlock(&radio->lock);
-
- return retval;
+ return v4l2_fh_release(file);
}
@@ -371,32 +349,25 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
goto err_initial;
}
- radio->users = 0;
radio->client = client;
mutex_init(&radio->lock);
- /* video device allocation and initialization */
- radio->videodev = video_device_alloc();
- if (!radio->videodev) {
- retval = -ENOMEM;
- goto err_radio;
- }
- memcpy(radio->videodev, &si470x_viddev_template,
- sizeof(si470x_viddev_template));
- video_set_drvdata(radio->videodev, radio);
+ /* video device initialization */
+ radio->videodev = si470x_viddev_template;
+ video_set_drvdata(&radio->videodev, radio);
/* power up : need 110ms */
radio->registers[POWERCFG] = POWERCFG_ENABLE;
if (si470x_set_register(radio, POWERCFG) < 0) {
retval = -EIO;
- goto err_video;
+ goto err_radio;
}
msleep(110);
/* get device and chip versions */
if (si470x_get_all_registers(radio) < 0) {
retval = -EIO;
- goto err_video;
+ goto err_radio;
}
dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[CHIPID]);
@@ -427,7 +398,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
retval = -EIO;
- goto err_video;
+ goto err_radio;
}
/* rds buffer configuration */
@@ -447,7 +418,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client,
}
/* register video device */
- retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
radio_nr);
if (retval) {
dev_warn(&client->dev, "Could not register video device\n");
@@ -460,8 +431,6 @@ err_all:
free_irq(client->irq, radio);
err_rds:
kfree(radio->buffer);
-err_video:
- video_device_release(radio->videodev);
err_radio:
kfree(radio);
err_initial:
@@ -477,7 +446,7 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client)
struct si470x_device *radio = i2c_get_clientdata(client);
free_irq(client->irq, radio);
- video_unregister_device(radio->videodev);
+ video_unregister_device(&radio->videodev);
kfree(radio);
return 0;
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index b7debb67932..e9f63876129 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -367,23 +367,6 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio)
/**************************************************************************
- * General Driver Functions - DISCONNECT_CHECK
- **************************************************************************/
-
-/*
- * si470x_disconnect_check - check whether radio disconnects
- */
-int si470x_disconnect_check(struct si470x_device *radio)
-{
- if (radio->disconnected)
- return -EIO;
- else
- return 0;
-}
-
-
-
-/**************************************************************************
* RDS Driver Functions
**************************************************************************/
@@ -414,9 +397,6 @@ static void si470x_int_in_callback(struct urb *urb)
}
}
- /* safety checks */
- if (radio->disconnected)
- return;
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
goto resubmit;
@@ -501,112 +481,30 @@ resubmit:
}
-
-/**************************************************************************
- * File Operations Interface
- **************************************************************************/
-
-/*
- * si470x_fops_open - file open
- */
int si470x_fops_open(struct file *file)
{
- struct si470x_device *radio = video_drvdata(file);
- int retval;
-
- mutex_lock(&radio->lock);
- radio->users++;
-
- retval = usb_autopm_get_interface(radio->intf);
- if (retval < 0) {
- radio->users--;
- retval = -EIO;
- goto done;
- }
-
- if (radio->users == 1) {
- /* start radio */
- retval = si470x_start(radio);
- if (retval < 0) {
- usb_autopm_put_interface(radio->intf);
- goto done;
- }
-
- /* initialize interrupt urb */
- usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
- usb_rcvintpipe(radio->usbdev,
- radio->int_in_endpoint->bEndpointAddress),
- radio->int_in_buffer,
- le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),
- si470x_int_in_callback,
- radio,
- radio->int_in_endpoint->bInterval);
-
- radio->int_in_running = 1;
- mb();
-
- retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);
- if (retval) {
- dev_info(&radio->intf->dev,
- "submitting int urb failed (%d)\n", retval);
- radio->int_in_running = 0;
- usb_autopm_put_interface(radio->intf);
- }
- }
-
-done:
- mutex_unlock(&radio->lock);
- return retval;
+ return v4l2_fh_open(file);
}
-
-/*
- * si470x_fops_release - file release
- */
int si470x_fops_release(struct file *file)
{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety check */
- if (!radio) {
- retval = -ENODEV;
- goto done;
- }
-
- mutex_lock(&radio->lock);
- radio->users--;
- if (radio->users == 0) {
- /* shutdown interrupt handler */
- if (radio->int_in_running) {
- radio->int_in_running = 0;
- if (radio->int_in_urb)
- usb_kill_urb(radio->int_in_urb);
- }
-
- if (radio->disconnected) {
- video_unregister_device(radio->videodev);
- kfree(radio->int_in_buffer);
- kfree(radio->buffer);
- mutex_unlock(&radio->lock);
- kfree(radio);
- goto done;
- }
+ return v4l2_fh_release(file);
+}
- /* cancel read processes */
- wake_up_interruptible(&radio->read_queue);
+static void si470x_usb_release(struct v4l2_device *v4l2_dev)
+{
+ struct si470x_device *radio =
+ container_of(v4l2_dev, struct si470x_device, v4l2_dev);
- /* stop radio */
- retval = si470x_stop(radio);
- usb_autopm_put_interface(radio->intf);
- }
- mutex_unlock(&radio->lock);
-done:
- return retval;
+ usb_free_urb(radio->int_in_urb);
+ v4l2_ctrl_handler_free(&radio->hdl);
+ v4l2_device_unregister(&radio->v4l2_dev);
+ kfree(radio->int_in_buffer);
+ kfree(radio->buffer);
+ kfree(radio);
}
-
/**************************************************************************
* Video4Linux Interface
**************************************************************************/
@@ -623,13 +521,45 @@ int si470x_vidioc_querycap(struct file *file, void *priv,
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
usb_make_path(radio->usbdev, capability->bus_info,
sizeof(capability->bus_info));
- capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ capability->device_caps = V4L2_CAP_HW_FREQ_SEEK |
V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
-
+ capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
+static int si470x_start_usb(struct si470x_device *radio)
+{
+ int retval;
+
+ /* start radio */
+ retval = si470x_start(radio);
+ if (retval < 0)
+ return retval;
+
+ v4l2_ctrl_handler_setup(&radio->hdl);
+
+ /* initialize interrupt urb */
+ usb_fill_int_urb(radio->int_in_urb, radio->usbdev,
+ usb_rcvintpipe(radio->usbdev,
+ radio->int_in_endpoint->bEndpointAddress),
+ radio->int_in_buffer,
+ le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize),
+ si470x_int_in_callback,
+ radio,
+ radio->int_in_endpoint->bInterval);
+
+ radio->int_in_running = 1;
+ mb();
+
+ retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL);
+ if (retval) {
+ dev_info(&radio->intf->dev,
+ "submitting int urb failed (%d)\n", retval);
+ radio->int_in_running = 0;
+ }
+ return retval;
+}
/**************************************************************************
* USB Interface
@@ -653,8 +583,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
retval = -ENOMEM;
goto err_initial;
}
- radio->users = 0;
- radio->disconnected = 0;
radio->usbdev = interface_to_usbdev(intf);
radio->intf = intf;
mutex_init(&radio->lock);
@@ -691,20 +619,35 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
goto err_intbuffer;
}
- /* video device allocation and initialization */
- radio->videodev = video_device_alloc();
- if (!radio->videodev) {
- retval = -ENOMEM;
+ radio->v4l2_dev.release = si470x_usb_release;
+ retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
+ if (retval < 0) {
+ dev_err(&intf->dev, "couldn't register v4l2_device\n");
goto err_urb;
}
- memcpy(radio->videodev, &si470x_viddev_template,
- sizeof(si470x_viddev_template));
- video_set_drvdata(radio->videodev, radio);
+
+ v4l2_ctrl_handler_init(&radio->hdl, 2);
+ v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15);
+ if (radio->hdl.error) {
+ retval = radio->hdl.error;
+ dev_err(&intf->dev, "couldn't register control\n");
+ goto err_dev;
+ }
+ radio->videodev = si470x_viddev_template;
+ radio->videodev.ctrl_handler = &radio->hdl;
+ radio->videodev.lock = &radio->lock;
+ radio->videodev.v4l2_dev = &radio->v4l2_dev;
+ radio->videodev.release = video_device_release_empty;
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags);
+ video_set_drvdata(&radio->videodev, radio);
/* get device and chip versions */
if (si470x_get_all_registers(radio) < 0) {
retval = -EIO;
- goto err_video;
+ goto err_ctrl;
}
dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[CHIPID]);
@@ -721,7 +664,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
/* get software and hardware versions */
if (si470x_get_scratch_page_versions(radio) < 0) {
retval = -EIO;
- goto err_video;
+ goto err_ctrl;
}
dev_info(&intf->dev, "software version %d, hardware version %d\n",
radio->software_version, radio->hardware_version);
@@ -764,28 +707,35 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
if (!radio->buffer) {
retval = -EIO;
- goto err_video;
+ goto err_ctrl;
}
/* rds buffer configuration */
radio->wr_index = 0;
radio->rd_index = 0;
init_waitqueue_head(&radio->read_queue);
+ usb_set_intfdata(intf, radio);
+
+ /* start radio */
+ retval = si470x_start_usb(radio);
+ if (retval < 0)
+ goto err_all;
/* register video device */
- retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO,
radio_nr);
if (retval) {
- dev_warn(&intf->dev, "Could not register video device\n");
+ dev_err(&intf->dev, "Could not register video device\n");
goto err_all;
}
- usb_set_intfdata(intf, radio);
return 0;
err_all:
kfree(radio->buffer);
-err_video:
- video_device_release(radio->videodev);
+err_ctrl:
+ v4l2_ctrl_handler_free(&radio->hdl);
+err_dev:
+ v4l2_device_unregister(&radio->v4l2_dev);
err_urb:
usb_free_urb(radio->int_in_urb);
err_intbuffer:
@@ -803,8 +753,22 @@ err_initial:
static int si470x_usb_driver_suspend(struct usb_interface *intf,
pm_message_t message)
{
+ struct si470x_device *radio = usb_get_intfdata(intf);
+
dev_info(&intf->dev, "suspending now...\n");
+ /* shutdown interrupt handler */
+ if (radio->int_in_running) {
+ radio->int_in_running = 0;
+ if (radio->int_in_urb)
+ usb_kill_urb(radio->int_in_urb);
+ }
+
+ /* cancel read processes */
+ wake_up_interruptible(&radio->read_queue);
+
+ /* stop radio */
+ si470x_stop(radio);
return 0;
}
@@ -814,9 +778,12 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf,
*/
static int si470x_usb_driver_resume(struct usb_interface *intf)
{
+ struct si470x_device *radio = usb_get_intfdata(intf);
+
dev_info(&intf->dev, "resuming now...\n");
- return 0;
+ /* start radio */
+ return si470x_start_usb(radio);
}
@@ -828,28 +795,22 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf)
struct si470x_device *radio = usb_get_intfdata(intf);
mutex_lock(&radio->lock);
- radio->disconnected = 1;
+ v4l2_device_disconnect(&radio->v4l2_dev);
+ video_unregister_device(&radio->videodev);
usb_set_intfdata(intf, NULL);
- if (radio->users == 0) {
- /* set led to disconnect state */
- si470x_set_led_state(radio, BLINK_ORANGE_LED);
-
- /* Free data structures. */
- usb_free_urb(radio->int_in_urb);
-
- kfree(radio->int_in_buffer);
- video_unregister_device(radio->videodev);
- kfree(radio->buffer);
- mutex_unlock(&radio->lock);
- kfree(radio);
- } else {
- mutex_unlock(&radio->lock);
- }
+ mutex_unlock(&radio->lock);
+ v4l2_device_put(&radio->v4l2_dev);
}
/*
* si470x_usb_driver - usb driver interface
+ *
+ * A note on suspend/resume: this driver had only empty suspend/resume
+ * functions, and when I tried to test suspend/resume it always disconnected
+ * instead of resuming (using my ADS InstantFM stick). So I've decided to
+ * remove these callbacks until someone else with better hardware can
+ * implement and test this.
*/
static struct usb_driver si470x_usb_driver = {
.name = DRIVER_NAME,
@@ -857,8 +818,8 @@ static struct usb_driver si470x_usb_driver = {
.disconnect = si470x_usb_driver_disconnect,
.suspend = si470x_usb_driver_suspend,
.resume = si470x_usb_driver_resume,
+ .reset_resume = si470x_usb_driver_resume,
.id_table = si470x_usb_driver_id_table,
- .supports_autosuspend = 1,
};
module_usb_driver(si470x_usb_driver);
diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h
index f300a55ed85..4921cab8e0f 100644
--- a/drivers/media/radio/si470x/radio-si470x.h
+++ b/drivers/media/radio/si470x/radio-si470x.h
@@ -36,6 +36,9 @@
#include <linux/mutex.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-device.h>
#include <asm/unaligned.h>
@@ -141,10 +144,9 @@
* si470x_device - private data
*/
struct si470x_device {
- struct video_device *videodev;
-
- /* driver management */
- unsigned int users;
+ struct v4l2_device v4l2_dev;
+ struct video_device videodev;
+ struct v4l2_ctrl_handler hdl;
/* Silabs internal registers (0..15) */
unsigned short registers[RADIO_REGISTER_NUM];
@@ -174,9 +176,6 @@ struct si470x_device {
/* scratch page */
unsigned char software_version;
unsigned char hardware_version;
-
- /* driver management */
- unsigned char disconnected;
#endif
#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
@@ -213,6 +212,7 @@ struct si470x_device {
* Common Functions
**************************************************************************/
extern struct video_device si470x_viddev_template;
+extern const struct v4l2_ctrl_ops si470x_ctrl_ops;
int si470x_get_register(struct si470x_device *radio, int regnr);
int si470x_set_register(struct si470x_device *radio, int regnr);
int si470x_disconnect_check(struct si470x_device *radio);
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
index 6418c4c9faf..06d47e5cce9 100644
--- a/drivers/media/radio/tef6862.c
+++ b/drivers/media/radio/tef6862.c
@@ -211,7 +211,7 @@ static struct i2c_driver tef6862_driver = {
.name = DRIVER_NAME,
},
.probe = tef6862_probe,
- .remove = tef6862_remove,
+ .remove = __devexit_p(tef6862_remove),
.id_table = tef6862_id,
};
diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c
index 077d369a017..080b96a61f1 100644
--- a/drivers/media/radio/wl128x/fmdrv_v4l2.c
+++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c
@@ -518,6 +518,10 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr)
video_set_drvdata(gradio_dev, fmdev);
gradio_dev->lock = &fmdev->mutex;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &gradio_dev->flags);
/* Register with V4L2 subsystem as RADIO device */
if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) {
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index a3fbb21350e..f97eeb87045 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -69,6 +69,7 @@ config IR_JVC_DECODER
config IR_SONY_DECODER
tristate "Enable IR raw decoder for the Sony protocol"
depends on RC_CORE
+ select BITREVERSE
default y
---help---
diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c
index baf907b3ce7..7be377fc1be 100644
--- a/drivers/media/rc/ati_remote.c
+++ b/drivers/media/rc/ati_remote.c
@@ -1,7 +1,7 @@
/*
* USB ATI Remote support
*
- * Copyright (c) 2011 Anssi Hannula <anssi.hannula@iki.fi>
+ * Copyright (c) 2011, 2012 Anssi Hannula <anssi.hannula@iki.fi>
* Version 2.2.0 Copyright (c) 2004 Torrey Hoffman <thoffman@arnor.net>
* Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev
*
@@ -151,13 +151,57 @@ MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes");
#undef err
#define err(format, arg...) printk(KERN_ERR format , ## arg)
+struct ati_receiver_type {
+ /* either default_keymap or get_default_keymap should be set */
+ const char *default_keymap;
+ const char *(*get_default_keymap)(struct usb_interface *interface);
+};
+
+static const char *get_medion_keymap(struct usb_interface *interface)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+
+ /*
+ * There are many different Medion remotes shipped with a receiver
+ * with the same usb id, but the receivers have subtle differences
+ * in the USB descriptors allowing us to detect them.
+ */
+
+ if (udev->manufacturer && udev->product) {
+ if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) {
+
+ if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc")
+ && !strcmp(udev->product, "USB Receiver"))
+ return RC_MAP_MEDION_X10_DIGITAINER;
+
+ if (!strcmp(udev->manufacturer, "X10 WTI")
+ && !strcmp(udev->product, "RF receiver"))
+ return RC_MAP_MEDION_X10_OR2X;
+ } else {
+
+ if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc")
+ && !strcmp(udev->product, "USB Receiver"))
+ return RC_MAP_MEDION_X10;
+ }
+ }
+
+ dev_info(&interface->dev,
+ "Unknown Medion X10 receiver, using default ati_remote Medion keymap\n");
+
+ return RC_MAP_MEDION_X10;
+}
+
+static const struct ati_receiver_type type_ati = { .default_keymap = RC_MAP_ATI_X10 };
+static const struct ati_receiver_type type_medion = { .get_default_keymap = get_medion_keymap };
+static const struct ati_receiver_type type_firefly = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY };
+
static struct usb_device_id ati_remote_table[] = {
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_MEDION_X10 },
- { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_SNAPSTREAM_FIREFLY },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_medion },
+ { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_firefly },
{} /* Terminating entry */
};
@@ -445,6 +489,7 @@ static void ati_remote_input_report(struct urb *urb)
int acc;
int remote_num;
unsigned char scancode;
+ u32 wheel_keycode = KEY_RESERVED;
int i;
/*
@@ -484,26 +529,33 @@ static void ati_remote_input_report(struct urb *urb)
*/
scancode = data[2] & 0x7f;
- /* Look up event code index in the mouse translation table. */
- for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
- if (scancode == ati_remote_tbl[i].data) {
- index = i;
- break;
+ dbginfo(&ati_remote->interface->dev,
+ "channel 0x%02x; key data %02x, scancode %02x\n",
+ remote_num, data[2], scancode);
+
+ if (scancode >= 0x70) {
+ /*
+ * This is either a mouse or scrollwheel event, depending on
+ * the remote/keymap.
+ * Get the keycode assigned to scancode 0x78/0x70. If it is
+ * set, assume this is a scrollwheel up/down event.
+ */
+ wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev,
+ scancode & 0x78);
+
+ if (wheel_keycode == KEY_RESERVED) {
+ /* scrollwheel was not mapped, assume mouse */
+
+ /* Look up event code index in the mouse translation table. */
+ for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) {
+ if (scancode == ati_remote_tbl[i].data) {
+ index = i;
+ break;
+ }
+ }
}
}
- if (index >= 0) {
- dbginfo(&ati_remote->interface->dev,
- "channel 0x%02x; mouse data %02x; index %d; keycode %d\n",
- remote_num, data[2], index, ati_remote_tbl[index].code);
- if (!dev)
- return; /* no mouse device */
- } else
- dbginfo(&ati_remote->interface->dev,
- "channel 0x%02x; key data %02x, scancode %02x\n",
- remote_num, data[2], scancode);
-
-
if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) {
input_event(dev, ati_remote_tbl[index].type,
ati_remote_tbl[index].code,
@@ -542,15 +594,29 @@ static void ati_remote_input_report(struct urb *urb)
if (index < 0) {
/* Not a mouse event, hand it to rc-core. */
-
- /*
- * We don't use the rc-core repeat handling yet as
- * it would cause ghost repeats which would be a
- * regression for this driver.
- */
- rc_keydown_notimeout(ati_remote->rdev, scancode,
- data[2]);
- rc_keyup(ati_remote->rdev);
+ int count = 1;
+
+ if (wheel_keycode != KEY_RESERVED) {
+ /*
+ * This is a scrollwheel event, send the
+ * scroll up (0x78) / down (0x70) scancode
+ * repeatedly as many times as indicated by
+ * rest of the scancode.
+ */
+ count = (scancode & 0x07) + 1;
+ scancode &= 0x78;
+ }
+
+ while (count--) {
+ /*
+ * We don't use the rc-core repeat handling yet as
+ * it would cause ghost repeats which would be a
+ * regression for this driver.
+ */
+ rc_keydown_notimeout(ati_remote->rdev, scancode,
+ data[2]);
+ rc_keyup(ati_remote->rdev);
+ }
return;
}
@@ -766,6 +832,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
struct usb_device *udev = interface_to_usbdev(interface);
struct usb_host_interface *iface_host = interface->cur_altsetting;
struct usb_endpoint_descriptor *endpoint_in, *endpoint_out;
+ struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info;
struct ati_remote *ati_remote;
struct input_dev *input_dev;
struct rc_dev *rc_dev;
@@ -827,10 +894,15 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name),
"%s mouse", ati_remote->rc_name);
- if (id->driver_info)
- rc_dev->map_name = (const char *)id->driver_info;
- else
- rc_dev->map_name = RC_MAP_ATI_X10;
+ rc_dev->map_name = RC_MAP_ATI_X10; /* default map */
+
+ /* set default keymap according to receiver model */
+ if (type) {
+ if (type->default_keymap)
+ rc_dev->map_name = type->default_keymap;
+ else if (type->get_default_keymap)
+ rc_dev->map_name = type->get_default_keymap(interface);
+ }
ati_remote_rc_init(ati_remote);
mutex_init(&ati_remote->open_mutex);
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 4a3a238bcfb..6aabf7ae3a3 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -556,11 +556,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,
FINTEK_DRIVER_NAME, (void *)fintek))
- goto failure;
+ goto failure2;
ret = rc_register_device(rdev);
if (ret)
- goto failure;
+ goto failure3;
device_init_wakeup(&pdev->dev, true);
fintek->rdev = rdev;
@@ -570,12 +570,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
return 0;
+failure3:
+ free_irq(fintek->cir_irq, fintek);
+failure2:
+ release_region(fintek->cir_addr, fintek->cir_port_len);
failure:
- if (fintek->cir_irq)
- free_irq(fintek->cir_irq, fintek);
- if (fintek->cir_addr)
- release_region(fintek->cir_addr, fintek->cir_port_len);
-
rc_free_device(rdev);
kfree(fintek);
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 7f26fdf2e54..5dd0386604f 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -255,7 +255,7 @@ static struct usb_device_id imon_usb_id_table[] = {
static struct usb_driver imon_driver = {
.name = MOD_NAME,
.probe = imon_probe,
- .disconnect = imon_disconnect,
+ .disconnect = __devexit_p(imon_disconnect),
.suspend = imon_suspend,
.resume = imon_resume,
.id_table = imon_usb_id_table,
diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c
index 95e630998aa..a8202512134 100644
--- a/drivers/media/rc/ir-raw.c
+++ b/drivers/media/rc/ir-raw.c
@@ -46,9 +46,9 @@ static int ir_raw_event_thread(void *data)
while (!kthread_should_stop()) {
spin_lock_irq(&raw->lock);
- retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
+ retval = kfifo_len(&raw->kfifo);
- if (!retval) {
+ if (retval < sizeof(ev)) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
@@ -59,11 +59,9 @@ static int ir_raw_event_thread(void *data)
continue;
}
+ retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev));
spin_unlock_irq(&raw->lock);
-
- BUG_ON(retval != sizeof(ev));
-
mutex_lock(&ir_raw_handler_lock);
list_for_each_entry(handler, &ir_raw_handler_list, list)
handler->decode(raw->dev, ev);
diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c
index d38fbdd0b25..7e54ec57bcf 100644
--- a/drivers/media/rc/ir-sanyo-decoder.c
+++ b/drivers/media/rc/ir-sanyo-decoder.c
@@ -56,7 +56,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
{
struct sanyo_dec *data = &dev->raw->sanyo;
u32 scancode;
- u8 address, not_address, command, not_command;
+ u8 address, command, not_command;
if (!(dev->raw->enabled_protocols & RC_TYPE_SANYO))
return 0;
@@ -154,7 +154,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev)
break;
address = bitrev16((data->bits >> 29) & 0x1fff) >> 3;
- not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3;
+ /* not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; */
command = bitrev8((data->bits >> 8) & 0xff);
not_command = bitrev8((data->bits >> 0) & 0xff);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 0e49c99abf6..36fe5a349b9 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1598,24 +1598,22 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED,
ITE_DRIVER_NAME, (void *)itdev))
- goto failure;
+ goto failure2;
ret = rc_register_device(rdev);
if (ret)
- goto failure;
+ goto failure3;
itdev->rdev = rdev;
ite_pr(KERN_NOTICE, "driver has been successfully loaded\n");
return 0;
+failure3:
+ free_irq(itdev->cir_irq, itdev);
+failure2:
+ release_region(itdev->cir_addr, itdev->params.io_region_size);
failure:
- if (itdev->cir_irq)
- free_irq(itdev->cir_irq, itdev);
-
- if (itdev->cir_addr)
- release_region(itdev->cir_addr, itdev->params.io_region_size);
-
rc_free_device(rdev);
kfree(itdev);
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 49ce2662f56..ab84d66c67c 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-anysee.o \
rc-apac-viewcomp.o \
rc-asus-pc39.o \
+ rc-asus-ps3-100.o \
rc-ati-tv-wonder-hd-600.o \
rc-ati-x10.o \
rc-avermedia-a16d.o \
@@ -52,6 +53,8 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-lme2510.o \
rc-manli.o \
rc-medion-x10.o \
+ rc-medion-x10-digitainer.o \
+ rc-medion-x10-or2x.o \
rc-msi-digivox-ii.o \
rc-msi-digivox-iii.o \
rc-msi-tvanywhere.o \
diff --git a/drivers/media/rc/keymaps/rc-asus-ps3-100.c b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
new file mode 100644
index 00000000000..ba76609c593
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-asus-ps3-100.c
@@ -0,0 +1,91 @@
+/* asus-ps3-100.h - Keytable for asus_ps3_100 Remote Controller
+ *
+ * Copyright (c) 2012 by Mauro Carvalho Chehab <mchehab@redhat.com>
+ *
+ * Based on a previous patch from Remi Schwartz <remi.schwartz@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.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+static struct rc_map_table asus_ps3_100[] = {
+ { 0x081c, KEY_HOME }, /* home */
+ { 0x081e, KEY_TV }, /* tv */
+ { 0x0803, KEY_TEXT }, /* teletext */
+ { 0x0829, KEY_POWER }, /* close */
+
+ { 0x080b, KEY_RED }, /* red */
+ { 0x080d, KEY_YELLOW }, /* yellow */
+ { 0x0806, KEY_BLUE }, /* blue */
+ { 0x0807, KEY_GREEN }, /* green */
+
+ /* Keys 0 to 9 */
+ { 0x082a, KEY_0 },
+ { 0x0816, KEY_1 },
+ { 0x0812, KEY_2 },
+ { 0x0814, KEY_3 },
+ { 0x0836, KEY_4 },
+ { 0x0832, KEY_5 },
+ { 0x0834, KEY_6 },
+ { 0x080e, KEY_7 },
+ { 0x080a, KEY_8 },
+ { 0x080c, KEY_9 },
+
+ { 0x0815, KEY_VOLUMEUP },
+ { 0x0826, KEY_VOLUMEDOWN },
+ { 0x0835, KEY_CHANNELUP }, /* channel / program + */
+ { 0x0824, KEY_CHANNELDOWN }, /* channel / program - */
+
+ { 0x0808, KEY_UP },
+ { 0x0804, KEY_DOWN },
+ { 0x0818, KEY_LEFT },
+ { 0x0810, KEY_RIGHT },
+ { 0x0825, KEY_ENTER }, /* enter */
+
+ { 0x0822, KEY_EXIT }, /* back */
+ { 0x082c, KEY_AB }, /* recall */
+
+ { 0x0820, KEY_AUDIO }, /* TV audio */
+ { 0x0837, KEY_SCREEN }, /* snapshot */
+ { 0x082e, KEY_ZOOM }, /* full screen */
+ { 0x0802, KEY_MUTE }, /* mute */
+
+ { 0x0831, KEY_REWIND }, /* backward << */
+ { 0x0811, KEY_RECORD }, /* recording */
+ { 0x0809, KEY_STOP },
+ { 0x0805, KEY_FASTFORWARD }, /* forward >> */
+ { 0x0821, KEY_PREVIOUS }, /* rew */
+ { 0x081a, KEY_PAUSE }, /* pause */
+ { 0x0839, KEY_PLAY }, /* play */
+ { 0x0819, KEY_NEXT }, /* forward */
+};
+
+static struct rc_map_list asus_ps3_100_map = {
+.map = {
+ .scan = asus_ps3_100,
+ .size = ARRAY_SIZE(asus_ps3_100),
+ .rc_type = RC_TYPE_RC5,
+ .name = RC_MAP_ASUS_PS3_100,
+}
+};
+
+static int __init init_rc_map_asus_ps3_100(void)
+{
+return rc_map_register(&asus_ps3_100_map);
+}
+
+static void __exit exit_rc_map_asus_ps3_100(void)
+{
+rc_map_unregister(&asus_ps3_100_map);
+}
+
+module_init(init_rc_map_asus_ps3_100)
+module_exit(exit_rc_map_asus_ps3_100)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c
index 28e376e18b9..bd42a30ec06 100644
--- a/drivers/media/rc/keymaps/rc-it913x-v2.c
+++ b/drivers/media/rc/keymaps/rc-it913x-v2.c
@@ -40,7 +40,7 @@ static struct rc_map_table it913x_v2_rc[] = {
/* Type 2 */
/* keys stereo, snapshot unassigned */
{ 0x866b00, KEY_0 },
- { 0x866b1b, KEY_1 },
+ { 0x866b01, KEY_1 },
{ 0x866b02, KEY_2 },
{ 0x866b03, KEY_3 },
{ 0x866b04, KEY_4 },
diff --git a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c
new file mode 100644
index 00000000000..966f9b3c71d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c
@@ -0,0 +1,123 @@
+/*
+ * Medion X10 RF remote keytable (Digitainer variant)
+ *
+ * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * This keymap is for a variant that has a distinctive scrollwheel instead of
+ * up/down buttons (tested with P/N 40009936 / 20018268), reportedly
+ * originally shipped with Medion Digitainer but now sold separately simply as
+ * an "X10" remote.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10_digitainer[] = {
+ { 0x02, KEY_POWER },
+
+ { 0x2c, KEY_TV },
+ { 0x2d, KEY_VIDEO },
+ { 0x04, KEY_DVD }, /* CD/DVD */
+ { 0x16, KEY_TEXT }, /* "teletext" icon, i.e. a screen with lines */
+ { 0x06, KEY_AUDIO },
+ { 0x2e, KEY_RADIO },
+ { 0x31, KEY_EPG }, /* a screen with an open book */
+ { 0x05, KEY_IMAGES }, /* Photo */
+ { 0x2f, KEY_INFO },
+
+ { 0x78, KEY_UP }, /* scrollwheel up 1 notch */
+ /* 0x79..0x7f: 2-8 notches, driver repeats 0x78 entry */
+
+ { 0x70, KEY_DOWN }, /* scrollwheel down 1 notch */
+ /* 0x71..0x77: 2-8 notches, driver repeats 0x70 entry */
+
+ { 0x19, KEY_MENU },
+ { 0x1d, KEY_LEFT },
+ { 0x1e, KEY_OK }, /* scrollwheel press */
+ { 0x1f, KEY_RIGHT },
+ { 0x20, KEY_BACK },
+
+ { 0x09, KEY_VOLUMEUP },
+ { 0x08, KEY_VOLUMEDOWN },
+ { 0x00, KEY_MUTE },
+
+ { 0x1b, KEY_SELECT }, /* also has "U" rotated 90 degrees CCW */
+
+ { 0x0b, KEY_CHANNELUP },
+ { 0x0c, KEY_CHANNELDOWN },
+ { 0x1c, KEY_LAST },
+
+ { 0x32, KEY_RED }, /* also Audio */
+ { 0x33, KEY_GREEN }, /* also Subtitle */
+ { 0x34, KEY_YELLOW }, /* also Angle */
+ { 0x35, KEY_BLUE }, /* also Title */
+
+ { 0x28, KEY_STOP },
+ { 0x29, KEY_PAUSE },
+ { 0x25, KEY_PLAY },
+ { 0x21, KEY_PREVIOUS },
+ { 0x18, KEY_CAMERA },
+ { 0x23, KEY_NEXT },
+ { 0x24, KEY_REWIND },
+ { 0x27, KEY_RECORD },
+ { 0x26, KEY_FORWARD },
+
+ { 0x0d, KEY_1 },
+ { 0x0e, KEY_2 },
+ { 0x0f, KEY_3 },
+ { 0x10, KEY_4 },
+ { 0x11, KEY_5 },
+ { 0x12, KEY_6 },
+ { 0x13, KEY_7 },
+ { 0x14, KEY_8 },
+ { 0x15, KEY_9 },
+ { 0x17, KEY_0 },
+
+ /* these do not actually exist on this remote, but these scancodes
+ * exist on all other Medion X10 remotes and adding them here allows
+ * such remotes to be adequately usable with this keymap in case
+ * this keymap is wrongly used with them (which is quite possible as
+ * there are lots of different Medion X10 remotes): */
+ { 0x1a, KEY_UP },
+ { 0x22, KEY_DOWN },
+};
+
+static struct rc_map_list medion_x10_digitainer_map = {
+ .map = {
+ .scan = medion_x10_digitainer,
+ .size = ARRAY_SIZE(medion_x10_digitainer),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_MEDION_X10_DIGITAINER,
+ }
+};
+
+static int __init init_rc_map_medion_x10_digitainer(void)
+{
+ return rc_map_register(&medion_x10_digitainer_map);
+}
+
+static void __exit exit_rc_map_medion_x10_digitainer(void)
+{
+ rc_map_unregister(&medion_x10_digitainer_map);
+}
+
+module_init(init_rc_map_medion_x10_digitainer)
+module_exit(exit_rc_map_medion_x10_digitainer)
+
+MODULE_DESCRIPTION("Medion X10 RF remote keytable (Digitainer variant)");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/keymaps/rc-medion-x10-or2x.c b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c
new file mode 100644
index 00000000000..b077300ecb5
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c
@@ -0,0 +1,108 @@
+/*
+ * Medion X10 OR22/OR24 RF remote keytable
+ *
+ * Copyright (C) 2012 Anssi Hannula <anssi.hannula@iki.fi>
+ *
+ * This keymap is for several Medion X10 remotes that have the Windows MCE
+ * button. This has been tested with a "RF VISTA Remote Control", OR24V,
+ * P/N 20035335, but should work with other variants that have the same
+ * buttons, such as OR22V and OR24E.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <media/rc-map.h>
+
+static struct rc_map_table medion_x10_or2x[] = {
+ { 0x02, KEY_POWER },
+ { 0x16, KEY_TEXT }, /* "T" in a box, for teletext */
+
+ { 0x09, KEY_VOLUMEUP },
+ { 0x08, KEY_VOLUMEDOWN },
+ { 0x00, KEY_MUTE },
+ { 0x0b, KEY_CHANNELUP },
+ { 0x0c, KEY_CHANNELDOWN },
+
+ { 0x32, KEY_RED },
+ { 0x33, KEY_GREEN },
+ { 0x34, KEY_YELLOW },
+ { 0x35, KEY_BLUE },
+
+ { 0x18, KEY_PVR }, /* record symbol inside a tv symbol */
+ { 0x04, KEY_DVD }, /* disc symbol */
+ { 0x31, KEY_EPG }, /* a tv schedule symbol */
+ { 0x1c, KEY_TV }, /* play symbol inside a tv symbol */
+ { 0x20, KEY_BACK },
+ { 0x2f, KEY_INFO },
+
+ { 0x1a, KEY_UP },
+ { 0x22, KEY_DOWN },
+ { 0x1d, KEY_LEFT },
+ { 0x1f, KEY_RIGHT },
+ { 0x1e, KEY_OK },
+
+ { 0x1b, KEY_MEDIA }, /* Windows MCE button */
+
+ { 0x21, KEY_PREVIOUS },
+ { 0x23, KEY_NEXT },
+ { 0x24, KEY_REWIND },
+ { 0x26, KEY_FORWARD },
+ { 0x25, KEY_PLAY },
+ { 0x28, KEY_STOP },
+ { 0x29, KEY_PAUSE },
+ { 0x27, KEY_RECORD },
+
+ { 0x0d, KEY_1 },
+ { 0x0e, KEY_2 },
+ { 0x0f, KEY_3 },
+ { 0x10, KEY_4 },
+ { 0x11, KEY_5 },
+ { 0x12, KEY_6 },
+ { 0x13, KEY_7 },
+ { 0x14, KEY_8 },
+ { 0x15, KEY_9 },
+ { 0x17, KEY_0 },
+ { 0x30, KEY_CLEAR },
+ { 0x36, KEY_ENTER },
+ { 0x37, KEY_NUMERIC_STAR },
+ { 0x38, KEY_NUMERIC_POUND },
+};
+
+static struct rc_map_list medion_x10_or2x_map = {
+ .map = {
+ .scan = medion_x10_or2x,
+ .size = ARRAY_SIZE(medion_x10_or2x),
+ .rc_type = RC_TYPE_OTHER,
+ .name = RC_MAP_MEDION_X10_OR2X,
+ }
+};
+
+static int __init init_rc_map_medion_x10_or2x(void)
+{
+ return rc_map_register(&medion_x10_or2x_map);
+}
+
+static void __exit exit_rc_map_medion_x10_or2x(void)
+{
+ rc_map_unregister(&medion_x10_or2x_map);
+}
+
+module_init(init_rc_map_medion_x10_or2x)
+module_exit(exit_rc_map_medion_x10_or2x)
+
+MODULE_DESCRIPTION("Medion X10 OR22/OR24 RF remote keytable");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index e150a2e29a4..84e06d3aa69 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -520,7 +520,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
{
char codes[USB_BUFLEN * 3 + 1];
char inout[9];
- u8 cmd, subcmd, data1, data2, data3, data4, data5;
+ u8 cmd, subcmd, data1, data2, data3, data4;
struct device *dev = ir->dev;
int i, start, skip = 0;
u32 carrier, period;
@@ -553,7 +553,6 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
data2 = buf[start + 3] & 0xff;
data3 = buf[start + 4] & 0xff;
data4 = buf[start + 5] & 0xff;
- data5 = buf[start + 6] & 0xff;
switch (cmd) {
case MCE_CMD_NULL:
@@ -1443,7 +1442,7 @@ static int mceusb_dev_resume(struct usb_interface *intf)
static struct usb_driver mceusb_dev_driver = {
.name = DRIVER_NAME,
.probe = mceusb_dev_probe,
- .disconnect = mceusb_dev_disconnect,
+ .disconnect = __devexit_p(mceusb_dev_disconnect),
.suspend = mceusb_dev_suspend,
.resume = mceusb_dev_resume,
.reset_resume = mceusb_dev_resume,
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index 8b2c071ac0a..dc8a7dddccd 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -1075,19 +1075,19 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED,
NVT_DRIVER_NAME, (void *)nvt))
- goto failure;
+ goto failure2;
if (!request_region(nvt->cir_wake_addr,
CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
- goto failure;
+ goto failure3;
if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED,
NVT_DRIVER_NAME, (void *)nvt))
- goto failure;
+ goto failure4;
ret = rc_register_device(rdev);
if (ret)
- goto failure;
+ goto failure5;
device_init_wakeup(&pdev->dev, true);
nvt->rdev = rdev;
@@ -1099,17 +1099,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
return 0;
+failure5:
+ free_irq(nvt->cir_wake_irq, nvt);
+failure4:
+ release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
+failure3:
+ free_irq(nvt->cir_irq, nvt);
+failure2:
+ release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
failure:
- if (nvt->cir_irq)
- free_irq(nvt->cir_irq, nvt);
- if (nvt->cir_addr)
- release_region(nvt->cir_addr, CIR_IOREG_LENGTH);
-
- if (nvt->cir_wake_irq)
- free_irq(nvt->cir_wake_irq, nvt);
- if (nvt->cir_wake_addr)
- release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH);
-
rc_free_device(rdev);
kfree(nvt);
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index efc6a514348..fae1615e0ff 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -221,7 +221,6 @@ static int __init loop_init(void)
rc->s_idle = loop_set_idle;
rc->s_learning_mode = loop_set_learning_mode;
rc->s_carrier_report = loop_set_carrier_report;
- rc->priv = &loopdev;
loopdev.txmask = RXMASK_REGULAR;
loopdev.txcarrier = 36000;
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index ad95c67a4db..2878b0ed974 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -1277,7 +1277,7 @@ static int redrat3_dev_resume(struct usb_interface *intf)
static struct usb_driver redrat3_dev_driver = {
.name = DRIVER_NAME,
.probe = redrat3_dev_probe,
- .disconnect = redrat3_dev_disconnect,
+ .disconnect = __devexit_p(redrat3_dev_disconnect),
.suspend = redrat3_dev_suspend,
.resume = redrat3_dev_resume,
.reset_resume = redrat3_dev_resume,
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index ce1e7ba940f..99937c94d7d 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -472,6 +472,9 @@ comment "Camera sensor devices"
config VIDEO_APTINA_PLL
tristate
+config VIDEO_SMIAPP_PLL
+ tristate
+
config VIDEO_OV7670
tristate "OmniVision OV7670 sensor support"
depends on I2C && VIDEO_V4L2
@@ -556,6 +559,8 @@ config VIDEO_S5K6AA
This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M
camera sensor with an embedded SoC image signal processor.
+source "drivers/media/video/smiapp/Kconfig"
+
comment "Flash devices"
config VIDEO_ADP1653
@@ -644,6 +649,8 @@ menuconfig V4L_USB_DRIVERS
if V4L_USB_DRIVERS
+source "drivers/media/video/au0828/Kconfig"
+
source "drivers/media/video/uvc/Kconfig"
source "drivers/media/video/gspca/Kconfig"
@@ -662,8 +669,6 @@ source "drivers/media/video/tm6000/Kconfig"
source "drivers/media/video/usbvision/Kconfig"
-source "drivers/media/video/et61x251/Kconfig"
-
source "drivers/media/video/sn9c102/Kconfig"
source "drivers/media/video/pwc/Kconfig"
@@ -721,8 +726,6 @@ menuconfig V4L_PCI_DRIVERS
if V4L_PCI_DRIVERS
-source "drivers/media/video/au0828/Kconfig"
-
source "drivers/media/video/bt8xx/Kconfig"
source "drivers/media/video/cx18/Kconfig"
@@ -794,6 +797,19 @@ source "drivers/media/video/saa7164/Kconfig"
source "drivers/media/video/zoran/Kconfig"
+config STA2X11_VIP
+ tristate "STA2X11 VIP Video For Linux"
+ depends on STA2X11
+ select VIDEO_ADV7180 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEOBUF_DMA_CONTIG
+ depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS
+ help
+ Say Y for support for STA2X11 VIP (Video Input Port) capture
+ device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sta2x11_vip.
+
endif # V4L_PCI_DRIVERS
#
@@ -1127,19 +1143,6 @@ config VIDEO_MX2
This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
Interface
-config VIDEO_SAMSUNG_S5P_FIMC
- tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)"
- depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \
- VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- ---help---
- This is a v4l2 driver for Samsung S5P and EXYNOS4 camera
- host interface and video postprocessor.
-
- To compile this driver as a module, choose M here: the
- module will be called s5p-fimc.
-
config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support"
depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91
@@ -1148,16 +1151,7 @@ config VIDEO_ATMEL_ISI
This module makes the ATMEL Image Sensor Interface available
as a v4l2 device.
-config VIDEO_S5P_MIPI_CSIS
- tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
- depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P
- depends on VIDEO_V4L2_SUBDEV_API && REGULATOR
- ---help---
- This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver.
-
- To compile this driver as a module, choose M here: the
- module will be called s5p-csis.
-
+source "drivers/media/video/s5p-fimc/Kconfig"
source "drivers/media/video/s5p-tv/Kconfig"
endif # V4L_PLATFORM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index a6282a3a6a8..d209de0e0ca 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -79,9 +79,12 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o
obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o
obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/
obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o
+obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/
obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o
obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o
+obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o
+
obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
@@ -120,6 +123,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000/
obj-$(CONFIG_VIDEO_MXB) += mxb.o
obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
+obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o
obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
@@ -152,7 +156,6 @@ obj-$(CONFIG_USB_ZR364XX) += zr364xx.o
obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o
obj-$(CONFIG_USB_SN9C102) += sn9c102/
-obj-$(CONFIG_USB_ET61X251) += et61x251/
obj-$(CONFIG_USB_PWC) += pwc/
obj-$(CONFIG_USB_GSPCA) += gspca/
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
index 5b045b4a66f..57e87090388 100644
--- a/drivers/media/video/adp1653.c
+++ b/drivers/media/video/adp1653.c
@@ -34,7 +34,6 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/version.h>
#include <media/adp1653.h>
#include <media/v4l2-device.h>
@@ -282,19 +281,19 @@ adp1653_init_device(struct adp1653_flash *flash)
return -EIO;
}
- mutex_lock(&flash->ctrls.lock);
+ mutex_lock(flash->ctrls.lock);
/* Reset faults before reading new ones. */
flash->fault = 0;
rval = adp1653_get_fault(flash);
- mutex_unlock(&flash->ctrls.lock);
+ mutex_unlock(flash->ctrls.lock);
if (rval > 0) {
dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval);
return -EIO;
}
- mutex_lock(&flash->ctrls.lock);
+ mutex_lock(flash->ctrls.lock);
rval = adp1653_update_hw(flash);
- mutex_unlock(&flash->ctrls.lock);
+ mutex_unlock(flash->ctrls.lock);
if (rval) {
dev_err(&client->dev,
"adp1653_update_hw failed at %s\n", __func__);
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index b8b6c4b0cad..174bffacf11 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -48,6 +48,7 @@
#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0
#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0
#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0
+#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f
#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04
#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5
@@ -55,9 +56,29 @@
#define ADV7180_AUTODETECT_ENABLE_REG 0x07
#define ADV7180_AUTODETECT_DEFAULT 0x7f
+#define ADV7180_CON_REG 0x08 /*Unsigned */
+#define CON_REG_MIN 0
+#define CON_REG_DEF 128
+#define CON_REG_MAX 255
+
+#define ADV7180_BRI_REG 0x0a /*Signed */
+#define BRI_REG_MIN -128
+#define BRI_REG_DEF 0
+#define BRI_REG_MAX 127
+
+#define ADV7180_HUE_REG 0x0b /*Signed, inverted */
+#define HUE_REG_MIN -127
+#define HUE_REG_DEF 0
+#define HUE_REG_MAX 128
+
#define ADV7180_ADI_CTRL_REG 0x0e
#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20
+#define ADV7180_PWR_MAN_REG 0x0f
+#define ADV7180_PWR_MAN_ON 0x04
+#define ADV7180_PWR_MAN_OFF 0x24
+#define ADV7180_PWR_MAN_RES 0x80
+
#define ADV7180_STATUS1_REG 0x10
#define ADV7180_STATUS1_IN_LOCK 0x01
#define ADV7180_STATUS1_AUTOD_MASK 0x70
@@ -78,6 +99,12 @@
#define ADV7180_ICONF1_PSYNC_ONLY 0x10
#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0
+#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */
+#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */
+#define SAT_REG_MIN 0
+#define SAT_REG_DEF 128
+#define SAT_REG_MAX 255
+
#define ADV7180_IRQ1_LOCK 0x01
#define ADV7180_IRQ1_UNLOCK 0x02
#define ADV7180_ISR1_ADI 0x42
@@ -90,6 +117,9 @@
#define ADV7180_IMR3_ADI 0x4C
#define ADV7180_IMR4_ADI 0x50
+#define ADV7180_NTSC_V_BIT_END_REG 0xE6
+#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F
+
struct adv7180_state {
struct v4l2_subdev sd;
struct work_struct work;
@@ -97,6 +127,11 @@ struct adv7180_state {
int irq;
v4l2_std_id curr_norm;
bool autodetect;
+ s8 brightness;
+ s16 hue;
+ u8 contrast;
+ u8 saturation;
+ u8 input;
};
static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
@@ -155,7 +190,7 @@ static u32 adv7180_status_to_v4l2(u8 status1)
}
static int __adv7180_status(struct i2c_client *client, u32 *status,
- v4l2_std_id *std)
+ v4l2_std_id *std)
{
int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG);
@@ -192,6 +227,36 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
return err;
}
+static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
+ u32 output, u32 config)
+{
+ struct adv7180_state *state = to_state(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (ret)
+ return ret;
+
+ /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept
+ * all inputs and let the card driver take care of validation
+ */
+ if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input)
+ goto out;
+
+ ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG);
+
+ if (ret < 0)
+ goto out;
+
+ ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_INPUT_CONTROL_REG, ret | input);
+ state->input = input;
+out:
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
{
struct adv7180_state *state = to_state(sd);
@@ -205,7 +270,7 @@ static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
}
static int adv7180_g_chip_ident(struct v4l2_subdev *sd,
- struct v4l2_dbg_chip_ident *chip)
+ struct v4l2_dbg_chip_ident *chip)
{
struct i2c_client *client = v4l2_get_subdevdata(sd);
@@ -222,9 +287,10 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
/* all standards -> autodetect */
if (std == V4L2_STD_ALL) {
- ret = i2c_smbus_write_byte_data(client,
- ADV7180_INPUT_CONTROL_REG,
- ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
+ ret =
+ i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
+ | state->input);
if (ret < 0)
goto out;
@@ -236,7 +302,8 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
goto out;
ret = i2c_smbus_write_byte_data(client,
- ADV7180_INPUT_CONTROL_REG, ret);
+ ADV7180_INPUT_CONTROL_REG,
+ ret | state->input);
if (ret < 0)
goto out;
@@ -249,14 +316,138 @@ out:
return ret;
}
+static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+{
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX,
+ 1, BRI_REG_DEF);
+ case V4L2_CID_HUE:
+ return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX,
+ 1, HUE_REG_DEF);
+ case V4L2_CID_CONTRAST:
+ return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX,
+ 1, CON_REG_DEF);
+ case V4L2_CID_SATURATION:
+ return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX,
+ 1, SAT_REG_DEF);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct adv7180_state *state = to_state(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = state->brightness;
+ break;
+ case V4L2_CID_HUE:
+ ctrl->value = state->hue;
+ break;
+ case V4L2_CID_CONTRAST:
+ ctrl->value = state->contrast;
+ break;
+ case V4L2_CID_SATURATION:
+ ctrl->value = state->saturation;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
+static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+{
+ struct adv7180_state *state = to_state(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = mutex_lock_interruptible(&state->mutex);
+ if (ret)
+ return ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if ((ctrl->value > BRI_REG_MAX)
+ || (ctrl->value < BRI_REG_MIN)) {
+ ret = -ERANGE;
+ break;
+ }
+ state->brightness = ctrl->value;
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_BRI_REG,
+ state->brightness);
+ break;
+ case V4L2_CID_HUE:
+ if ((ctrl->value > HUE_REG_MAX)
+ || (ctrl->value < HUE_REG_MIN)) {
+ ret = -ERANGE;
+ break;
+ }
+ state->hue = ctrl->value;
+ /*Hue is inverted according to HSL chart */
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_HUE_REG, -state->hue);
+ break;
+ case V4L2_CID_CONTRAST:
+ if ((ctrl->value > CON_REG_MAX)
+ || (ctrl->value < CON_REG_MIN)) {
+ ret = -ERANGE;
+ break;
+ }
+ state->contrast = ctrl->value;
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_CON_REG,
+ state->contrast);
+ break;
+ case V4L2_CID_SATURATION:
+ if ((ctrl->value > SAT_REG_MAX)
+ || (ctrl->value < SAT_REG_MIN)) {
+ ret = -ERANGE;
+ break;
+ }
+ /*
+ *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
+ *Let's not confuse the user, everybody understands saturation
+ */
+ state->saturation = ctrl->value;
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_SD_SAT_CB_REG,
+ state->saturation);
+ if (ret < 0)
+ break;
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_SD_SAT_CR_REG,
+ state->saturation);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&state->mutex);
+ return ret;
+}
+
static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
+ .s_routing = adv7180_s_routing,
};
static const struct v4l2_subdev_core_ops adv7180_core_ops = {
.g_chip_ident = adv7180_g_chip_ident,
.s_std = adv7180_s_std,
+ .queryctrl = adv7180_queryctrl,
+ .g_ctrl = adv7180_g_ctrl,
+ .s_ctrl = adv7180_s_ctrl,
};
static const struct v4l2_subdev_ops adv7180_ops = {
@@ -267,13 +458,13 @@ static const struct v4l2_subdev_ops adv7180_ops = {
static void adv7180_work(struct work_struct *work)
{
struct adv7180_state *state = container_of(work, struct adv7180_state,
- work);
+ work);
struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
u8 isr3;
mutex_lock(&state->mutex);
i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
- ADV7180_ADI_CTRL_IRQ_SPACE);
+ ADV7180_ADI_CTRL_IRQ_SPACE);
isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI);
/* clear */
i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3);
@@ -297,56 +488,51 @@ static irqreturn_t adv7180_irq(int irq, void *devid)
return IRQ_HANDLED;
}
-/*
- * Generic i2c probe
- * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
- */
-
-static __devinit int adv7180_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int init_device(struct i2c_client *client, struct adv7180_state *state)
{
- struct adv7180_state *state;
- struct v4l2_subdev *sd;
int ret;
- /* 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%02x (%s)\n",
- client->addr << 1, client->adapter->name);
-
- state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
- if (state == NULL) {
- ret = -ENOMEM;
- goto err;
- }
-
- state->irq = client->irq;
- INIT_WORK(&state->work, adv7180_work);
- mutex_init(&state->mutex);
- state->autodetect = true;
- sd = &state->sd;
- v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
-
/* Initialize adv7180 */
/* Enable autodetection */
- ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
- ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM);
- if (ret < 0)
- goto err_unreg_subdev;
+ if (state->autodetect) {
+ ret =
+ i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
+ ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM
+ | state->input);
+ if (ret < 0)
+ return ret;
- ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG,
- ADV7180_AUTODETECT_DEFAULT);
- if (ret < 0)
- goto err_unreg_subdev;
+ ret =
+ i2c_smbus_write_byte_data(client,
+ ADV7180_AUTODETECT_ENABLE_REG,
+ ADV7180_AUTODETECT_DEFAULT);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = v4l2_std_to_adv7180(state->curr_norm);
+ if (ret < 0)
+ return ret;
+ ret =
+ i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG,
+ ret | state->input);
+ if (ret < 0)
+ return ret;
+
+ }
/* ITU-R BT.656-4 compatible */
ret = i2c_smbus_write_byte_data(client,
- ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
- ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
+ ADV7180_EXTENDED_OUTPUT_CONTROL_REG,
+ ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
+
+ /* Manually set V bit end position in NTSC mode */
+ ret = i2c_smbus_write_byte_data(client,
+ ADV7180_NTSC_V_BIT_END_REG,
+ ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
+ if (ret < 0)
+ return ret;
/* read current norm */
__adv7180_status(client, NULL, &state->curr_norm);
@@ -354,45 +540,109 @@ static __devinit int adv7180_probe(struct i2c_client *client,
/* register for interrupts */
if (state->irq > 0) {
ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME,
- state);
+ state);
if (ret)
- goto err_unreg_subdev;
+ return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
- ADV7180_ADI_CTRL_IRQ_SPACE);
+ ADV7180_ADI_CTRL_IRQ_SPACE);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
/* config the Interrupt pin to be active low */
ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI,
- ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY);
+ ADV7180_ICONF1_ACTIVE_LOW |
+ ADV7180_ICONF1_PSYNC_ONLY);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
/* enable AD change interrupts interrupts */
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI,
- ADV7180_IRQ3_AD_CHANGE);
+ ADV7180_IRQ3_AD_CHANGE);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG,
- 0);
+ 0);
if (ret < 0)
- goto err_unreg_subdev;
+ return ret;
}
+ /*Set default value for controls */
+ ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG,
+ state->brightness);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG,
+ state->contrast);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG,
+ state->saturation);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG,
+ state->saturation);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static __devinit int adv7180_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7180_state *state;
+ struct v4l2_subdev *sd;
+ int ret;
+
+ /* 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%02x (%s)\n",
+ client->addr, client->adapter->name);
+
+ state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL);
+ if (state == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ state->irq = client->irq;
+ INIT_WORK(&state->work, adv7180_work);
+ mutex_init(&state->mutex);
+ state->autodetect = true;
+ state->brightness = BRI_REG_DEF;
+ state->hue = HUE_REG_DEF;
+ state->contrast = CON_REG_DEF;
+ state->saturation = SAT_REG_DEF;
+ state->input = 0;
+ sd = &state->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
+
+ ret = init_device(client, state);
+ if (0 != ret)
+ goto err_unreg_subdev;
return 0;
err_unreg_subdev:
@@ -432,16 +682,49 @@ static const struct i2c_device_id adv7180_id[] = {
{},
};
+#ifdef CONFIG_PM
+static int adv7180_suspend(struct i2c_client *client, pm_message_t state)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
+ ADV7180_PWR_MAN_OFF);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int adv7180_resume(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7180_state *state = to_state(sd);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG,
+ ADV7180_PWR_MAN_ON);
+ if (ret < 0)
+ return ret;
+ ret = init_device(client, state);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+#endif
+
MODULE_DEVICE_TABLE(i2c, adv7180_id);
static struct i2c_driver adv7180_driver = {
.driver = {
- .owner = THIS_MODULE,
- .name = DRIVER_NAME,
- },
- .probe = adv7180_probe,
- .remove = __devexit_p(adv7180_remove),
- .id_table = adv7180_id,
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+ .probe = adv7180_probe,
+ .remove = __devexit_p(adv7180_remove),
+#ifdef CONFIG_PM
+ .suspend = adv7180_suspend,
+ .resume = adv7180_resume,
+#endif
+ .id_table = adv7180_id,
};
module_i2c_driver(adv7180_driver);
diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c
index 119b60401bf..2b5aa676a84 100644
--- a/drivers/media/video/adv7343.c
+++ b/drivers/media/video/adv7343.c
@@ -130,14 +130,12 @@ static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std)
{
struct adv7343_state *state = to_state(sd);
struct adv7343_std_info *std_info;
- int output_idx, num_std;
+ int num_std;
char *fsc_ptr;
u8 reg, val;
int err = 0;
int i = 0;
- output_idx = state->output;
-
std_info = (struct adv7343_std_info *)stdinfo;
num_std = ARRAY_SIZE(stdinfo);
diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c
index 0bd3813bb59..8153a449846 100644
--- a/drivers/media/video/aptina-pll.c
+++ b/drivers/media/video/aptina-pll.c
@@ -148,9 +148,8 @@ int aptina_pll_calculate(struct device *dev,
unsigned int mf_high;
unsigned int mf_low;
- mf_low = max(roundup(mf_min, mf_inc),
- DIV_ROUND_UP(pll->ext_clock * p1,
- limits->int_clock_max * div));
+ mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1,
+ limits->int_clock_max * div)), mf_inc);
mf_high = min(mf_max, pll->ext_clock * p1 /
(limits->int_clock_min * div));
diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c
index b6ed44aebe3..e346d32d08c 100644
--- a/drivers/media/video/arv.c
+++ b/drivers/media/video/arv.c
@@ -31,6 +31,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
#include <linux/mutex.h>
#include <asm/uaccess.h>
@@ -403,7 +404,8 @@ static int ar_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card));
strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info));
- vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -709,6 +711,8 @@ static int ar_initialize(struct ar *ar)
static const struct v4l2_file_operations ar_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
.read = ar_read,
.unlocked_ioctl = video_ioctl2,
};
@@ -769,6 +773,7 @@ static int __init ar_init(void)
ar->vdev.fops = &ar_fops;
ar->vdev.ioctl_ops = &ar_ioctl_ops;
ar->vdev.release = video_device_release_empty;
+ set_bit(V4L2_FL_USE_FH_PRIO, &ar->vdev.flags);
video_set_drvdata(&ar->vdev, ar);
if (vga) {
diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c
index 7a3371f044f..c4b03572dce 100644
--- a/drivers/media/video/as3645a.c
+++ b/drivers/media/video/as3645a.c
@@ -713,7 +713,7 @@ static int as3645a_resume(struct device *dev)
* The number of LEDs reported in platform data is used to compute default
* limits. Parameters passed through platform data can override those limits.
*/
-static int as3645a_init_controls(struct as3645a *flash)
+static int __devinit as3645a_init_controls(struct as3645a *flash)
{
const struct as3645a_platform_data *pdata = flash->pdata;
struct v4l2_ctrl *ctrl;
@@ -804,8 +804,8 @@ static int as3645a_init_controls(struct as3645a *flash)
return flash->ctrls.error;
}
-static int as3645a_probe(struct i2c_client *client,
- const struct i2c_device_id *devid)
+static int __devinit as3645a_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
{
struct as3645a *flash;
int ret;
@@ -846,7 +846,7 @@ done:
return ret;
}
-static int __exit as3645a_remove(struct i2c_client *client)
+static int __devexit as3645a_remove(struct i2c_client *client)
{
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct as3645a *flash = to_as3645a(subdev);
@@ -877,7 +877,7 @@ static struct i2c_driver as3645a_i2c_driver = {
.pm = &as3645a_pm_ops,
},
.probe = as3645a_probe,
- .remove = __exit_p(as3645a_remove),
+ .remove = __devexit_p(as3645a_remove),
.id_table = as3645a_id_table,
};
diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c
index ec3f6a06f9c..6274a91c25c 100644
--- a/drivers/media/video/atmel-isi.c
+++ b/drivers/media/video/atmel-isi.c
@@ -260,7 +260,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct atmel_isi *isi = ici->priv;
unsigned long size;
- int ret, bytes_per_line;
+ int ret;
/* Reset ISI */
ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
@@ -271,13 +271,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
/* Disable all interrupts */
isi_writel(isi, ISI_INTDIS, ~0UL);
- bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
-
- size = bytes_per_line * icd->user_height;
+ size = icd->sizeimage;
if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM)
*nbuffers = MAX_BUFFER_NUM;
@@ -316,13 +310,8 @@ static int buffer_prepare(struct vb2_buffer *vb)
struct atmel_isi *isi = ici->priv;
unsigned long size;
struct isi_dma_desc *desc;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
- size = bytes_per_line * icd->user_height;
+ size = icd->sizeimage;
if (vb2_plane_size(vb, 0) < size) {
dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n",
@@ -638,6 +627,7 @@ static const struct soc_mbus_pixelfmt isi_camera_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
};
diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig
index 81ba9d9d1b5..23f7fd22f0e 100644
--- a/drivers/media/video/au0828/Kconfig
+++ b/drivers/media/video/au0828/Kconfig
@@ -6,7 +6,8 @@ config VIDEO_AU0828
select I2C_ALGOBIT
select VIDEO_TVEEPROM
select VIDEOBUF_VMALLOC
- select DVB_AU8522 if !DVB_FE_CUSTOMISE
+ select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE
+ select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c
index 1c6015a04f9..e3fe9a6637f 100644
--- a/drivers/media/video/au0828/au0828-cards.c
+++ b/drivers/media/video/au0828/au0828-cards.c
@@ -325,6 +325,8 @@ struct usb_device_id au0828_usb_id_table[] = {
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
{ USB_DEVICE(0x2040, 0x7281),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
+ { USB_DEVICE(0x05e1, 0x0480),
+ .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
{ USB_DEVICE(0x2040, 0x8200),
.driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
{ USB_DEVICE(0x2040, 0x7260),
diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c
index 518216743c9..39ece8e2498 100644
--- a/drivers/media/video/au0828/au0828-dvb.c
+++ b/drivers/media/video/au0828/au0828-dvb.c
@@ -25,6 +25,7 @@
#include <linux/device.h>
#include <linux/suspend.h>
#include <media/v4l2-common.h>
+#include <media/tuner.h>
#include "au0828.h"
#include "au8522.h"
@@ -79,9 +80,16 @@ static struct au8522_config hauppauge_woodbury_config = {
.vsb_if = AU8522_IF_3_25MHZ,
};
-static struct xc5000_config hauppauge_hvr950q_tunerconfig = {
+static struct xc5000_config hauppauge_xc5000a_config = {
.i2c_address = 0x61,
.if_khz = 6000,
+ .chip_id = XC5000A,
+};
+
+static struct xc5000_config hauppauge_xc5000c_config = {
+ .i2c_address = 0x61,
+ .if_khz = 6000,
+ .chip_id = XC5000C,
};
static struct mxl5007t_config mxl5007t_hvr950q_config = {
@@ -383,8 +391,19 @@ int au0828_dvb_register(struct au0828_dev *dev)
&hauppauge_hvr950q_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
- dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap,
- &hauppauge_hvr950q_tunerconfig);
+ switch (dev->board.tuner_type) {
+ default:
+ case TUNER_XC5000:
+ dvb_attach(xc5000_attach, dvb->frontend,
+ &dev->i2c_adap,
+ &hauppauge_xc5000a_config);
+ break;
+ case TUNER_XC5000C:
+ dvb_attach(xc5000_attach, dvb->frontend,
+ &dev->i2c_adap,
+ &hauppauge_xc5000c_config);
+ break;
+ }
break;
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
dvb->frontend = dvb_attach(au8522_attach,
@@ -411,7 +430,7 @@ int au0828_dvb_register(struct au0828_dev *dev)
if (dvb->frontend != NULL) {
dvb_attach(xc5000_attach, dvb->frontend,
&dev->i2c_adap,
- &hauppauge_hvr950q_tunerconfig);
+ &hauppauge_xc5000a_config);
}
break;
default:
diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
index 0b3e481ffe8..ac3dd733ab8 100644
--- a/drivers/media/video/au0828/au0828-video.c
+++ b/drivers/media/video/au0828/au0828-video.c
@@ -120,7 +120,7 @@ static void au0828_irq_callback(struct urb *urb)
struct au0828_dmaqueue *dma_q = urb->context;
struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
unsigned long flags = 0;
- int rc, i;
+ int i;
switch (urb->status) {
case 0: /* success */
@@ -138,7 +138,7 @@ static void au0828_irq_callback(struct urb *urb)
/* Copy data from URB */
spin_lock_irqsave(&dev->slock, flags);
- rc = dev->isoc_ctl.isoc_copy(dev, urb);
+ dev->isoc_ctl.isoc_copy(dev, urb);
spin_unlock_irqrestore(&dev->slock, flags);
/* Reset urb buffers */
@@ -1881,7 +1881,7 @@ int au0828_analog_register(struct au0828_dev *dev,
int retval = -ENOMEM;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
- int i;
+ int i, ret;
dprintk(1, "au0828_analog_register called!\n");
@@ -1951,8 +1951,8 @@ int au0828_analog_register(struct au0828_dev *dev,
dev->vbi_dev = video_device_alloc();
if (NULL == dev->vbi_dev) {
dprintk(1, "Can't allocate vbi_device.\n");
- kfree(dev->vdev);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_vdev;
}
/* Fill the video capture device struct */
@@ -1971,8 +1971,8 @@ int au0828_analog_register(struct au0828_dev *dev,
if (retval != 0) {
dprintk(1, "unable to register video device (error = %d).\n",
retval);
- video_device_release(dev->vdev);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_vbi_dev;
}
/* Register the vbi device */
@@ -1981,13 +1981,18 @@ int au0828_analog_register(struct au0828_dev *dev,
if (retval != 0) {
dprintk(1, "unable to register vbi device (error = %d).\n",
retval);
- video_device_release(dev->vbi_dev);
- video_device_release(dev->vdev);
- return -ENODEV;
+ ret = -ENODEV;
+ goto err_vbi_dev;
}
dprintk(1, "%s completed!\n", __func__);
return 0;
+
+err_vbi_dev:
+ video_device_release(dev->vbi_dev);
+err_vdev:
+ video_device_release(dev->vdev);
+ return ret;
}
diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
index 514fcf742f5..0aba45e34f7 100644
--- a/drivers/media/video/blackfin/bfin_capture.c
+++ b/drivers/media/video/blackfin/bfin_capture.c
@@ -942,6 +942,10 @@ static int __devinit bcap_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&bcap_dev->dma_queue);
vfd->lock = &bcap_dev->mutex;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
/* register video device */
ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index e581b37be78..a9cfb0f4be4 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -663,7 +663,7 @@ static const struct v4l2_queryctrl bttv_ctls[] = {
.minimum = 0,
.maximum = 65535,
.step = 128,
- .default_value = 32768,
+ .default_value = 27648,
.type = V4L2_CTRL_TYPE_INTEGER,
},{
.id = V4L2_CID_SATURATION,
@@ -4394,7 +4394,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
if (!bttv_tvcards[btv->c.type].no_video) {
bttv_register_video(btv);
bt848_bright(btv,32768);
- bt848_contrast(btv,32768);
+ bt848_contrast(btv, 27648);
bt848_hue(btv,32768);
bt848_sat(btv,32768);
audio_mute(btv, 1);
diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c
index f09df9dffaa..2520219f01b 100644
--- a/drivers/media/video/bw-qcam.c
+++ b/drivers/media/video/bw-qcam.c
@@ -77,6 +77,9 @@ OTHER DEALINGS IN THE SOFTWARE.
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
/* One from column A... */
#define QC_NOTSET 0
@@ -103,6 +106,7 @@ OTHER DEALINGS IN THE SOFTWARE.
struct qcam {
struct v4l2_device v4l2_dev;
struct video_device vdev;
+ struct v4l2_ctrl_handler hdl;
struct pardevice *pdev;
struct parport *pport;
struct mutex lock;
@@ -646,7 +650,8 @@ static int qcam_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "B&W Quickcam", sizeof(vcap->card));
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -674,72 +679,6 @@ static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
return (inp > 0) ? -EINVAL : 0;
}
-static int qcam_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 180);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192);
- case V4L2_CID_GAMMA:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 105);
- }
- return -EINVAL;
-}
-
-static int qcam_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct qcam *qcam = video_drvdata(file);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = qcam->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = qcam->contrast;
- break;
- case V4L2_CID_GAMMA:
- ctrl->value = qcam->whitebal;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int qcam_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct qcam *qcam = video_drvdata(file);
- int ret = 0;
-
- mutex_lock(&qcam->lock);
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- qcam->brightness = ctrl->value;
- break;
- case V4L2_CID_CONTRAST:
- qcam->contrast = ctrl->value;
- break;
- case V4L2_CID_GAMMA:
- qcam->whitebal = ctrl->value;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- if (ret == 0) {
- qc_setscanmode(qcam);
- qcam->status |= QC_PARAM_CHANGE;
- }
- mutex_unlock(&qcam->lock);
- return ret;
-}
-
static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct qcam *qcam = video_drvdata(file);
@@ -856,8 +795,40 @@ static ssize_t qcam_read(struct file *file, char __user *buf,
return len;
}
+static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct qcam *qcam =
+ container_of(ctrl->handler, struct qcam, hdl);
+ int ret = 0;
+
+ mutex_lock(&qcam->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ qcam->brightness = ctrl->val;
+ break;
+ case V4L2_CID_CONTRAST:
+ qcam->contrast = ctrl->val;
+ break;
+ case V4L2_CID_GAMMA:
+ qcam->whitebal = ctrl->val;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret == 0) {
+ qc_setscanmode(qcam);
+ qcam->status |= QC_PARAM_CHANGE;
+ }
+ mutex_unlock(&qcam->lock);
+ return ret;
+}
+
static const struct v4l2_file_operations qcam_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
.read = qcam_read,
};
@@ -867,13 +838,17 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
.vidioc_g_input = qcam_g_input,
.vidioc_s_input = qcam_s_input,
.vidioc_enum_input = qcam_enum_input,
- .vidioc_queryctrl = qcam_queryctrl,
- .vidioc_g_ctrl = qcam_g_ctrl,
- .vidioc_s_ctrl = qcam_s_ctrl,
.vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ctrl_ops qcam_ctrl_ops = {
+ .s_ctrl = qcam_s_ctrl,
};
/* Initialize the QuickCam driver control structure. This is where
@@ -897,19 +872,35 @@ static struct qcam *qcam_init(struct parport *port)
return NULL;
}
+ v4l2_ctrl_handler_init(&qcam->hdl, 3);
+ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 180);
+ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 192);
+ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 255, 1, 105);
+ if (qcam->hdl.error) {
+ v4l2_err(v4l2_dev, "couldn't register controls\n");
+ v4l2_ctrl_handler_free(&qcam->hdl);
+ kfree(qcam);
+ return NULL;
+ }
qcam->pport = port;
qcam->pdev = parport_register_device(port, "bw-qcam", NULL, NULL,
NULL, 0, NULL);
if (qcam->pdev == NULL) {
v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
+ v4l2_ctrl_handler_free(&qcam->hdl);
kfree(qcam);
return NULL;
}
strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name));
qcam->vdev.v4l2_dev = v4l2_dev;
+ qcam->vdev.ctrl_handler = &qcam->hdl;
qcam->vdev.fops = &qcam_fops;
qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
+ set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags);
qcam->vdev.release = video_device_release_empty;
video_set_drvdata(&qcam->vdev, qcam);
@@ -1003,6 +994,7 @@ static int init_bwqcam(struct parport *port)
static void close_bwqcam(struct qcam *qcam)
{
video_unregister_device(&qcam->vdev);
+ v4l2_ctrl_handler_free(&qcam->hdl);
parport_unregister_device(qcam->pdev);
kfree(qcam);
}
diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c
index fda32f52554..ec51e1f12e8 100644
--- a/drivers/media/video/c-qcam.c
+++ b/drivers/media/video/c-qcam.c
@@ -40,10 +40,14 @@
#include <media/v4l2-device.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
struct qcam {
struct v4l2_device v4l2_dev;
struct video_device vdev;
+ struct v4l2_ctrl_handler hdl;
struct pardevice *pdev;
struct parport *pport;
int width, height;
@@ -378,7 +382,7 @@ get_fragment:
static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
{
struct v4l2_device *v4l2_dev = &qcam->v4l2_dev;
- unsigned lines, pixelsperline, bitsperxfer;
+ unsigned lines, pixelsperline;
unsigned int is_bi_dir = qcam->bidirectional;
size_t wantlen, outptr = 0;
char tmpbuf[BUFSZ];
@@ -404,7 +408,6 @@ static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len)
lines = qcam->height;
pixelsperline = qcam->width;
- bitsperxfer = (is_bi_dir) ? 24 : 8;
if (is_bi_dir) {
/* Turn the port around */
@@ -516,7 +519,8 @@ static int qcam_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card));
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -544,73 +548,6 @@ static int qcam_s_input(struct file *file, void *fh, unsigned int inp)
return (inp > 0) ? -EINVAL : 0;
}
-static int qcam_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 240);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192);
- case V4L2_CID_GAMMA:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- }
- return -EINVAL;
-}
-
-static int qcam_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct qcam *qcam = video_drvdata(file);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = qcam->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = qcam->contrast;
- break;
- case V4L2_CID_GAMMA:
- ctrl->value = qcam->whitebal;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int qcam_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct qcam *qcam = video_drvdata(file);
- int ret = 0;
-
- mutex_lock(&qcam->lock);
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- qcam->brightness = ctrl->value;
- break;
- case V4L2_CID_CONTRAST:
- qcam->contrast = ctrl->value;
- break;
- case V4L2_CID_GAMMA:
- qcam->whitebal = ctrl->value;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- if (ret == 0) {
- parport_claim_or_block(qcam->pdev);
- qc_setup(qcam);
- parport_release(qcam->pdev);
- }
- mutex_unlock(&qcam->lock);
- return ret;
-}
-
static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
{
struct qcam *qcam = video_drvdata(file);
@@ -714,8 +651,41 @@ static ssize_t qcam_read(struct file *file, char __user *buf,
return len;
}
+static int qcam_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct qcam *qcam =
+ container_of(ctrl->handler, struct qcam, hdl);
+ int ret = 0;
+
+ mutex_lock(&qcam->lock);
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ qcam->brightness = ctrl->val;
+ break;
+ case V4L2_CID_CONTRAST:
+ qcam->contrast = ctrl->val;
+ break;
+ case V4L2_CID_GAMMA:
+ qcam->whitebal = ctrl->val;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret == 0) {
+ parport_claim_or_block(qcam->pdev);
+ qc_setup(qcam);
+ parport_release(qcam->pdev);
+ }
+ mutex_unlock(&qcam->lock);
+ return ret;
+}
+
static const struct v4l2_file_operations qcam_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
.read = qcam_read,
};
@@ -725,13 +695,17 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = {
.vidioc_g_input = qcam_g_input,
.vidioc_s_input = qcam_s_input,
.vidioc_enum_input = qcam_enum_input,
- .vidioc_queryctrl = qcam_queryctrl,
- .vidioc_g_ctrl = qcam_g_ctrl,
- .vidioc_s_ctrl = qcam_s_ctrl,
- .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ctrl_ops qcam_ctrl_ops = {
+ .s_ctrl = qcam_s_ctrl,
};
/* Initialize the QuickCam driver control structure. */
@@ -754,6 +728,20 @@ static struct qcam *qcam_init(struct parport *port)
return NULL;
}
+ v4l2_ctrl_handler_init(&qcam->hdl, 3);
+ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 240);
+ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 192);
+ v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 255, 1, 128);
+ if (qcam->hdl.error) {
+ v4l2_err(v4l2_dev, "couldn't register controls\n");
+ v4l2_ctrl_handler_free(&qcam->hdl);
+ kfree(qcam);
+ return NULL;
+ }
+
qcam->pport = port;
qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL,
NULL, 0, NULL);
@@ -762,6 +750,7 @@ static struct qcam *qcam_init(struct parport *port)
if (qcam->pdev == NULL) {
v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name);
+ v4l2_ctrl_handler_free(&qcam->hdl);
kfree(qcam);
return NULL;
}
@@ -771,6 +760,8 @@ static struct qcam *qcam_init(struct parport *port)
qcam->vdev.fops = &qcam_fops;
qcam->vdev.ioctl_ops = &qcam_ioctl_ops;
qcam->vdev.release = video_device_release_empty;
+ qcam->vdev.ctrl_handler = &qcam->hdl;
+ set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags);
video_set_drvdata(&qcam->vdev, qcam);
mutex_init(&qcam->lock);
@@ -845,6 +836,7 @@ static int init_cqcam(struct parport *port)
static void close_cqcam(struct qcam *qcam)
{
video_unregister_device(&qcam->vdev);
+ v4l2_ctrl_handler_free(&qcam->hdl);
parport_unregister_device(qcam->pdev);
kfree(qcam);
}
diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h
index ab252188981..cdef677d57e 100644
--- a/drivers/media/video/cpia2/cpia2.h
+++ b/drivers/media/video/cpia2/cpia2.h
@@ -32,11 +32,12 @@
#define __CPIA2_H__
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
#include <linux/usb.h>
#include <linux/poll.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
-#include "cpia2dev.h"
#include "cpia2_registers.h"
/* define for verbose debug output */
@@ -65,7 +66,6 @@
/* Flicker Modes */
#define NEVER_FLICKER 0
-#define ANTI_FLICKER_ON 1
#define FLICKER_60 60
#define FLICKER_50 50
@@ -148,7 +148,6 @@ enum {
#define DEFAULT_BRIGHTNESS 0x46
#define DEFAULT_CONTRAST 0x93
#define DEFAULT_SATURATION 0x7f
-#define DEFAULT_TARGET_KB 0x30
/* Power state */
#define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER
@@ -287,7 +286,6 @@ struct camera_params {
struct {
u8 cam_register;
u8 flicker_mode_req; /* 1 if flicker on, else never flicker */
- int mains_frequency;
} flicker_control;
struct {
@@ -337,7 +335,7 @@ struct camera_params {
u8 vc_control;
u8 vc_mp_direction;
u8 vc_mp_data;
- u8 target_kb;
+ u8 quality;
} vc_params;
struct {
@@ -366,23 +364,23 @@ struct framebuf {
struct framebuf *next;
};
-struct cpia2_fh {
- enum v4l2_priority prio;
- u8 mmapped;
-};
-
struct camera_data {
/* locks */
+ struct v4l2_device v4l2_dev;
struct mutex v4l2_lock; /* serialize file operations */
- struct v4l2_prio_state prio;
+ struct v4l2_ctrl_handler hdl;
+ struct {
+ /* Lights control cluster */
+ struct v4l2_ctrl *top_light;
+ struct v4l2_ctrl *bottom_light;
+ };
+ struct v4l2_ctrl *usb_alt;
/* camera status */
- volatile int present; /* Is the camera still present? */
- int open_count; /* # of process that have camera open */
int first_image_seen;
- u8 mains_freq; /* for flicker control */
enum sensors sensor_type;
u8 flush;
+ struct v4l2_fh *stream_fh;
u8 mmapped;
int streaming; /* 0 = no, 1 = yes */
int xfer_mode; /* XFER_BULK or XFER_ISOC */
@@ -390,7 +388,7 @@ struct camera_data {
/* v4l */
int video_size; /* VIDEO_SIZE_ */
- struct video_device *vdev; /* v4l videodev */
+ struct video_device vdev; /* v4l videodev */
u32 width;
u32 height; /* Its size */
__u32 pixelformat; /* Format fourcc */
@@ -425,6 +423,7 @@ struct camera_data {
/* v4l */
int cpia2_register_camera(struct camera_data *cam);
void cpia2_unregister_camera(struct camera_data *cam);
+void cpia2_camera_release(struct v4l2_device *v4l2_dev);
/* core */
int cpia2_reset_camera(struct camera_data *cam);
@@ -443,7 +442,7 @@ int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd);
int cpia2_do_command(struct camera_data *cam,
unsigned int command,
unsigned char direction, unsigned char param);
-struct camera_data *cpia2_init_camera_struct(void);
+struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf);
int cpia2_init_camera(struct camera_data *cam);
int cpia2_allocate_buffers(struct camera_data *cam);
void cpia2_free_buffers(struct camera_data *cam);
@@ -454,7 +453,6 @@ unsigned int cpia2_poll(struct camera_data *cam,
int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma);
void cpia2_set_property_flip(struct camera_data *cam, int prop_val);
void cpia2_set_property_mirror(struct camera_data *cam, int prop_val);
-int cpia2_set_target_kb(struct camera_data *cam, unsigned char value);
int cpia2_set_gpio(struct camera_data *cam, unsigned char setting);
int cpia2_set_fps(struct camera_data *cam, int framerate);
diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c
index ee91e295c90..17188e23477 100644
--- a/drivers/media/video/cpia2/cpia2_core.c
+++ b/drivers/media/video/cpia2/cpia2_core.c
@@ -66,7 +66,6 @@ static int config_sensor_410(struct camera_data *cam,
static int config_sensor_500(struct camera_data *cam,
int reqwidth, int reqheight);
static int set_all_properties(struct camera_data *cam);
-static void get_color_params(struct camera_data *cam);
static void wake_system(struct camera_data *cam);
static void set_lowlight_boost(struct camera_data *cam);
static void reset_camera_struct(struct camera_data *cam);
@@ -453,15 +452,6 @@ int cpia2_do_command(struct camera_data *cam,
cam->params.version.vp_device_hi = cmd.buffer.block_data[0];
cam->params.version.vp_device_lo = cmd.buffer.block_data[1];
break;
- case CPIA2_CMD_GET_VP_BRIGHTNESS:
- cam->params.color_params.brightness = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_CONTRAST:
- cam->params.color_params.contrast = cmd.buffer.block_data[0];
- break;
- case CPIA2_CMD_GET_VP_SATURATION:
- cam->params.color_params.saturation = cmd.buffer.block_data[0];
- break;
case CPIA2_CMD_GET_VP_GPIO_DATA:
cam->params.vp_params.gpio_data = cmd.buffer.block_data[0];
break;
@@ -617,6 +607,7 @@ int cpia2_reset_camera(struct camera_data *cam)
{
u8 tmp_reg;
int retval = 0;
+ int target_kb;
int i;
struct cpia2_command cmd;
@@ -800,9 +791,16 @@ int cpia2_reset_camera(struct camera_data *cam)
}
cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg);
- /* Set target size (kb) on vc */
+ /* Set target size (kb) on vc
+ This is a heuristic based on the quality parameter and the raw
+ framesize in kB divided by 16 (the compression factor when the
+ quality is 100%) */
+ target_kb = (cam->width * cam->height * 2 / 16384) *
+ cam->params.vc_params.quality / 100;
+ if (target_kb < 1)
+ target_kb = 1;
cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB,
- TRANSFER_WRITE, cam->params.vc_params.target_kb);
+ TRANSFER_WRITE, target_kb);
/* Wiggle VC Reset */
/***
@@ -1538,23 +1536,17 @@ static int set_all_properties(struct camera_data *cam)
* framerate and user_mode were already set (set_default_user_mode).
**/
- cpia2_set_color_params(cam);
-
cpia2_usb_change_streaming_alternate(cam,
cam->params.camera_state.stream_mode);
- cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
- cam->params.vp_params.user_effects);
-
- cpia2_set_flicker_mode(cam,
- cam->params.flicker_control.flicker_mode_req);
-
cpia2_do_command(cam,
CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION,
TRANSFER_WRITE, cam->params.vp_params.gpio_direction);
cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE,
cam->params.vp_params.gpio_data);
+ v4l2_ctrl_handler_setup(&cam->hdl);
+
wake_system(cam);
set_lowlight_boost(cam);
@@ -1569,7 +1561,6 @@ static int set_all_properties(struct camera_data *cam)
*****************************************************************************/
void cpia2_save_camera_state(struct camera_data *cam)
{
- get_color_params(cam);
cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0);
cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ,
0);
@@ -1577,30 +1568,6 @@ void cpia2_save_camera_state(struct camera_data *cam)
/* Don't get framerate or target_kb. Trust the values we already have */
}
-/******************************************************************************
- *
- * get_color_params
- *
- *****************************************************************************/
-static void get_color_params(struct camera_data *cam)
-{
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0);
- cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, TRANSFER_READ, 0);
-}
-
-/******************************************************************************
- *
- * cpia2_set_color_params
- *
- *****************************************************************************/
-void cpia2_set_color_params(struct camera_data *cam)
-{
- DBG("Setting color params\n");
- cpia2_set_brightness(cam, cam->params.color_params.brightness);
- cpia2_set_contrast(cam, cam->params.color_params.contrast);
- cpia2_set_saturation(cam, cam->params.color_params.saturation);
-}
/******************************************************************************
*
@@ -1664,15 +1631,9 @@ int cpia2_set_flicker_mode(struct camera_data *cam, int mode)
switch(mode) {
case NEVER_FLICKER:
- cam->params.flicker_control.flicker_mode_req = mode;
- break;
case FLICKER_60:
- cam->params.flicker_control.flicker_mode_req = mode;
- cam->params.flicker_control.mains_frequency = 60;
- break;
case FLICKER_50:
cam->params.flicker_control.flicker_mode_req = mode;
- cam->params.flicker_control.mains_frequency = 50;
break;
default:
err = -EINVAL;
@@ -1701,6 +1662,7 @@ void cpia2_set_property_flip(struct camera_data *cam, int prop_val)
{
cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP;
}
+ cam->params.vp_params.user_effects = cam_reg;
cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
cam_reg);
}
@@ -1725,37 +1687,13 @@ void cpia2_set_property_mirror(struct camera_data *cam, int prop_val)
{
cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR;
}
+ cam->params.vp_params.user_effects = cam_reg;
cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
cam_reg);
}
/******************************************************************************
*
- * set_target_kb
- *
- * The new Target KB is set in cam->params.vc_params.target_kb and
- * activates on reset.
- *****************************************************************************/
-
-int cpia2_set_target_kb(struct camera_data *cam, unsigned char value)
-{
- DBG("Requested target_kb = %d\n", value);
- if (value != cam->params.vc_params.target_kb) {
-
- cpia2_usb_stream_pause(cam);
-
- /* reset camera for new target_kb */
- cam->params.vc_params.target_kb = value;
- cpia2_reset_camera(cam);
-
- cpia2_usb_stream_resume(cam);
- }
-
- return 0;
-}
-
-/******************************************************************************
- *
* cpia2_set_gpio
*
*****************************************************************************/
@@ -1843,7 +1781,7 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value)
if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0)
value++;
DBG("Setting brightness to %d (0x%0x)\n", value, value);
- cpia2_do_command(cam,CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE,value);
+ cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value);
}
/******************************************************************************
@@ -1854,7 +1792,6 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value)
void cpia2_set_contrast(struct camera_data *cam, unsigned char value)
{
DBG("Setting contrast to %d (0x%0x)\n", value, value);
- cam->params.color_params.contrast = value;
cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value);
}
@@ -1866,7 +1803,6 @@ void cpia2_set_contrast(struct camera_data *cam, unsigned char value)
void cpia2_set_saturation(struct camera_data *cam, unsigned char value)
{
DBG("Setting saturation to %d (0x%0x)\n", value, value);
- cam->params.color_params.saturation = value;
cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value);
}
@@ -2168,14 +2104,10 @@ static void reset_camera_struct(struct camera_data *cam)
/***
* The following parameter values are the defaults from the register map.
***/
- cam->params.color_params.brightness = DEFAULT_BRIGHTNESS;
- cam->params.color_params.contrast = DEFAULT_CONTRAST;
- cam->params.color_params.saturation = DEFAULT_SATURATION;
cam->params.vp_params.lowlight_boost = 0;
/* FlickerModes */
cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER;
- cam->params.flicker_control.mains_frequency = 60;
/* jpeg params */
cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT;
@@ -2188,7 +2120,7 @@ static void reset_camera_struct(struct camera_data *cam)
cam->params.vp_params.gpio_data = 0;
/* Target kb params */
- cam->params.vc_params.target_kb = DEFAULT_TARGET_KB;
+ cam->params.vc_params.quality = 100;
/***
* Set Sensor FPS as fast as possible.
@@ -2228,7 +2160,7 @@ static void reset_camera_struct(struct camera_data *cam)
*
* Initializes camera struct, does not call reset to fill in defaults.
*****************************************************************************/
-struct camera_data *cpia2_init_camera_struct(void)
+struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf)
{
struct camera_data *cam;
@@ -2239,8 +2171,13 @@ struct camera_data *cpia2_init_camera_struct(void)
return NULL;
}
+ cam->v4l2_dev.release = cpia2_camera_release;
+ if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) {
+ v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n");
+ kfree(cam);
+ return NULL;
+ }
- cam->present = 1;
mutex_init(&cam->v4l2_lock);
init_waitqueue_head(&cam->wq_stream);
@@ -2373,11 +2310,6 @@ long cpia2_read(struct camera_data *cam,
return -EINVAL;
}
- if (!cam->present) {
- LOG("%s: camera removed\n",__func__);
- return 0; /* EOF */
- }
-
if (!cam->streaming) {
/* Start streaming */
cpia2_usb_stream_start(cam,
@@ -2393,12 +2325,12 @@ long cpia2_read(struct camera_data *cam,
if (frame->status != FRAME_READY) {
mutex_unlock(&cam->v4l2_lock);
wait_event_interruptible(cam->wq_stream,
- !cam->present ||
+ !video_is_registered(&cam->vdev) ||
(frame = cam->curbuff)->status == FRAME_READY);
mutex_lock(&cam->v4l2_lock);
if (signal_pending(current))
return -ERESTARTSYS;
- if (!cam->present)
+ if (!video_is_registered(&cam->vdev))
return 0;
}
@@ -2423,17 +2355,10 @@ long cpia2_read(struct camera_data *cam,
unsigned int cpia2_poll(struct camera_data *cam, struct file *filp,
poll_table *wait)
{
- unsigned int status=0;
+ unsigned int status = v4l2_ctrl_poll(filp, wait);
- if (!cam) {
- ERR("%s: Internal error, camera_data not found!\n",__func__);
- return POLLERR;
- }
-
- if (!cam->present)
- return POLLHUP;
-
- if(!cam->streaming) {
+ if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) &&
+ !cam->streaming) {
/* Start streaming */
cpia2_usb_stream_start(cam,
cam->params.camera_state.stream_mode);
@@ -2441,10 +2366,8 @@ unsigned int cpia2_poll(struct camera_data *cam, struct file *filp,
poll_wait(filp, &cam->wq_stream, wait);
- if(!cam->present)
- status = POLLHUP;
- else if(cam->curbuff->status == FRAME_READY)
- status = POLLIN | POLLRDNORM;
+ if (cam->curbuff->status == FRAME_READY)
+ status |= POLLIN | POLLRDNORM;
return status;
}
@@ -2462,12 +2385,9 @@ int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma)
unsigned long start = (unsigned long) adr;
unsigned long page, pos;
- if (!cam)
- return -ENODEV;
-
DBG("mmap offset:%ld size:%ld\n", start_offset, size);
- if (!cam->present)
+ if (!video_is_registered(&cam->vdev))
return -ENODEV;
if (size > cam->frame_size*cam->num_frames ||
diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c
index 59c797c1527..95b5d6e7cdc 100644
--- a/drivers/media/video/cpia2/cpia2_usb.c
+++ b/drivers/media/video/cpia2/cpia2_usb.c
@@ -54,6 +54,8 @@ static void cpia2_usb_complete(struct urb *urb);
static int cpia2_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id);
static void cpia2_usb_disconnect(struct usb_interface *intf);
+static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message);
+static int cpia2_usb_resume(struct usb_interface *intf);
static void free_sbufs(struct camera_data *cam);
static void add_APPn(struct camera_data *cam);
@@ -74,6 +76,9 @@ static struct usb_driver cpia2_driver = {
.name = "cpia2",
.probe = cpia2_usb_probe,
.disconnect = cpia2_usb_disconnect,
+ .suspend = cpia2_usb_suspend,
+ .resume = cpia2_usb_resume,
+ .reset_resume = cpia2_usb_resume,
.id_table = cpia2_id_table
};
@@ -218,10 +223,9 @@ static void cpia2_usb_complete(struct urb *urb)
return;
}
- if (!cam->streaming || !cam->present || cam->open_count == 0) {
- LOG("Will now stop the streaming: streaming = %d, "
- "present=%d, open_count=%d\n",
- cam->streaming, cam->present, cam->open_count);
+ if (!cam->streaming || !video_is_registered(&cam->vdev)) {
+ LOG("Will now stop the streaming: streaming = %d, present=%d\n",
+ cam->streaming, video_is_registered(&cam->vdev));
return;
}
@@ -392,7 +396,7 @@ static int configure_transfer_mode(struct camera_data *cam, unsigned int alt)
struct cpia2_command cmd;
unsigned char reg;
- if(!cam->present)
+ if (!video_is_registered(&cam->vdev))
return -ENODEV;
/***
@@ -752,8 +756,8 @@ int cpia2_usb_stream_pause(struct camera_data *cam)
{
int ret = 0;
if(cam->streaming) {
- ret = set_alternate(cam, USBIF_CMDONLY);
free_sbufs(cam);
+ ret = set_alternate(cam, USBIF_CMDONLY);
}
return ret;
}
@@ -770,6 +774,10 @@ int cpia2_usb_stream_resume(struct camera_data *cam)
cam->first_image_seen = 0;
ret = set_alternate(cam, cam->params.camera_state.stream_mode);
if(ret == 0) {
+ /* for some reason the user effects need to be set
+ again when starting streaming. */
+ cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
+ cam->params.vp_params.user_effects);
ret = submit_urbs(cam);
}
}
@@ -784,6 +792,7 @@ int cpia2_usb_stream_resume(struct camera_data *cam)
int cpia2_usb_stream_stop(struct camera_data *cam)
{
int ret;
+
ret = cpia2_usb_stream_pause(cam);
cam->streaming = 0;
configure_transfer_mode(cam, 0);
@@ -812,7 +821,8 @@ static int cpia2_usb_probe(struct usb_interface *intf,
/* If we get to this point, we found a CPiA2 camera */
LOG("CPiA2 USB camera found\n");
- if((cam = cpia2_init_camera_struct()) == NULL)
+ cam = cpia2_init_camera_struct(intf);
+ if (cam == NULL)
return -ENOMEM;
cam->dev = udev;
@@ -825,16 +835,9 @@ static int cpia2_usb_probe(struct usb_interface *intf,
return ret;
}
- if ((ret = cpia2_register_camera(cam)) < 0) {
- ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret);
- kfree(cam);
- return ret;
- }
-
if((ret = cpia2_init_camera(cam)) < 0) {
ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret);
- cpia2_unregister_camera(cam);
kfree(cam);
return ret;
}
@@ -853,6 +856,13 @@ static int cpia2_usb_probe(struct usb_interface *intf,
usb_set_intfdata(intf, cam);
+ ret = cpia2_register_camera(cam);
+ if (ret < 0) {
+ ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret);
+ kfree(cam);
+ return ret;
+ }
+
return 0;
}
@@ -865,13 +875,16 @@ static void cpia2_usb_disconnect(struct usb_interface *intf)
{
struct camera_data *cam = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
- cam->present = 0;
DBG("Stopping stream\n");
cpia2_usb_stream_stop(cam);
+ mutex_lock(&cam->v4l2_lock);
DBG("Unregistering camera\n");
cpia2_unregister_camera(cam);
+ v4l2_device_disconnect(&cam->v4l2_dev);
+ mutex_unlock(&cam->v4l2_lock);
+ v4l2_device_put(&cam->v4l2_dev);
if(cam->buffers) {
DBG("Wakeup waiting processes\n");
@@ -884,14 +897,41 @@ static void cpia2_usb_disconnect(struct usb_interface *intf)
DBG("Releasing interface\n");
usb_driver_release_interface(&cpia2_driver, intf);
- if (cam->open_count == 0) {
- DBG("Freeing camera structure\n");
- kfree(cam);
+ LOG("CPiA2 camera disconnected.\n");
+}
+
+static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct camera_data *cam = usb_get_intfdata(intf);
+
+ mutex_lock(&cam->v4l2_lock);
+ if (cam->streaming) {
+ cpia2_usb_stream_stop(cam);
+ cam->streaming = 1;
}
+ mutex_unlock(&cam->v4l2_lock);
- LOG("CPiA2 camera disconnected.\n");
+ dev_info(&intf->dev, "going into suspend..\n");
+ return 0;
}
+/* Resume device - start device. */
+static int cpia2_usb_resume(struct usb_interface *intf)
+{
+ struct camera_data *cam = usb_get_intfdata(intf);
+
+ mutex_lock(&cam->v4l2_lock);
+ v4l2_ctrl_handler_setup(&cam->hdl);
+ if (cam->streaming) {
+ cam->streaming = 0;
+ cpia2_usb_stream_start(cam,
+ cam->params.camera_state.stream_mode);
+ }
+ mutex_unlock(&cam->v4l2_lock);
+
+ dev_info(&intf->dev, "coming out of suspend..\n");
+ return 0;
+}
/******************************************************************************
*
diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
index 077eb1db80a..55e92902a76 100644
--- a/drivers/media/video/cpia2/cpia2_v4l.c
+++ b/drivers/media/video/cpia2/cpia2_v4l.c
@@ -39,15 +39,15 @@
#include <linux/videodev2.h>
#include <linux/stringify.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
#include "cpia2.h"
-#include "cpia2dev.h"
static int video_nr = -1;
module_param(video_nr, int, 0);
-MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)");
+MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)");
-static int buffer_size = 68*1024;
+static int buffer_size = 68 * 1024;
module_param(buffer_size, int, 0);
MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)");
@@ -62,18 +62,10 @@ MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-"
__stringify(USBIF_ISO_6) ", default "
__stringify(DEFAULT_ALT) ")");
-static int flicker_freq = 60;
-module_param(flicker_freq, int, 0);
-MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" __stringify(50) "or"
- __stringify(60) ", default "
- __stringify(60) ")");
-
-static int flicker_mode = NEVER_FLICKER;
+static int flicker_mode;
module_param(flicker_mode, int, 0);
-MODULE_PARM_DESC(flicker_mode,
- "Flicker supression (" __stringify(NEVER_FLICKER) "or"
- __stringify(ANTI_FLICKER_ON) ", default "
- __stringify(NEVER_FLICKER) ")");
+MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or "
+ __stringify(60) ", default 0)");
MODULE_AUTHOR("Steve Miller (STMicroelectronics) <steve.miller@st.com>");
MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras");
@@ -82,153 +74,7 @@ MODULE_LICENSE("GPL");
MODULE_VERSION(CPIA_VERSION);
#define ABOUT "V4L-Driver for Vision CPiA2 based cameras"
-
-struct control_menu_info {
- int value;
- char name[32];
-};
-
-static struct control_menu_info framerate_controls[] =
-{
- { CPIA2_VP_FRAMERATE_6_25, "6.25 fps" },
- { CPIA2_VP_FRAMERATE_7_5, "7.5 fps" },
- { CPIA2_VP_FRAMERATE_12_5, "12.5 fps" },
- { CPIA2_VP_FRAMERATE_15, "15 fps" },
- { CPIA2_VP_FRAMERATE_25, "25 fps" },
- { CPIA2_VP_FRAMERATE_30, "30 fps" },
-};
-#define NUM_FRAMERATE_CONTROLS (ARRAY_SIZE(framerate_controls))
-
-static struct control_menu_info flicker_controls[] =
-{
- { NEVER_FLICKER, "Off" },
- { FLICKER_50, "50 Hz" },
- { FLICKER_60, "60 Hz" },
-};
-#define NUM_FLICKER_CONTROLS (ARRAY_SIZE(flicker_controls))
-
-static struct control_menu_info lights_controls[] =
-{
- { 0, "Off" },
- { 64, "Top" },
- { 128, "Bottom" },
- { 192, "Both" },
-};
-#define NUM_LIGHTS_CONTROLS (ARRAY_SIZE(lights_controls))
-#define GPIO_LIGHTS_MASK 192
-
-static struct v4l2_queryctrl controls[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = DEFAULT_BRIGHTNESS,
- },
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = DEFAULT_CONTRAST,
- },
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = DEFAULT_SATURATION,
- },
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror Horizontally",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flip Vertically",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = CPIA2_CID_TARGET_KB,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Target KB",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = DEFAULT_TARGET_KB,
- },
- {
- .id = CPIA2_CID_GPIO,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "GPIO",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = CPIA2_CID_FLICKER_MODE,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Flicker Reduction",
- .minimum = 0,
- .maximum = NUM_FLICKER_CONTROLS-1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = CPIA2_CID_FRAMERATE,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Framerate",
- .minimum = 0,
- .maximum = NUM_FRAMERATE_CONTROLS-1,
- .step = 1,
- .default_value = NUM_FRAMERATE_CONTROLS-1,
- },
- {
- .id = CPIA2_CID_USB_ALT,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "USB Alternate",
- .minimum = USBIF_ISO_1,
- .maximum = USBIF_ISO_6,
- .step = 1,
- .default_value = DEFAULT_ALT,
- },
- {
- .id = CPIA2_CID_LIGHTS,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Lights",
- .minimum = 0,
- .maximum = NUM_LIGHTS_CONTROLS-1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = CPIA2_CID_RESET_CAMERA,
- .type = V4L2_CTRL_TYPE_BUTTON,
- .name = "Reset Camera",
- .minimum = 0,
- .maximum = 0,
- .step = 0,
- .default_value = 0,
- },
-};
-#define NUM_CONTROLS (ARRAY_SIZE(controls))
-
+#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000)
/******************************************************************************
*
@@ -238,38 +84,27 @@ static struct v4l2_queryctrl controls[] = {
static int cpia2_open(struct file *file)
{
struct camera_data *cam = video_drvdata(file);
- struct cpia2_fh *fh;
-
- if (!cam) {
- ERR("Internal error, camera_data not found!\n");
- return -ENODEV;
- }
+ int retval = v4l2_fh_open(file);
- if (!cam->present)
- return -ENODEV;
+ if (retval)
+ return retval;
- if (cam->open_count == 0) {
- if (cpia2_allocate_buffers(cam))
+ if (v4l2_fh_is_singular_file(file)) {
+ if (cpia2_allocate_buffers(cam)) {
+ v4l2_fh_release(file);
return -ENOMEM;
+ }
/* reset the camera */
- if (cpia2_reset_camera(cam) < 0)
+ if (cpia2_reset_camera(cam) < 0) {
+ v4l2_fh_release(file);
return -EIO;
+ }
cam->APP_len = 0;
cam->COM_len = 0;
}
- fh = kmalloc(sizeof(*fh), GFP_KERNEL);
- if (!fh)
- return -ENOMEM;
- file->private_data = fh;
- fh->prio = V4L2_PRIORITY_UNSET;
- v4l2_prio_open(&cam->prio, &fh->prio);
- fh->mmapped = 0;
-
- ++cam->open_count;
-
cpia2_dbg_dump_registers(cam);
return 0;
}
@@ -283,37 +118,22 @@ static int cpia2_close(struct file *file)
{
struct video_device *dev = video_devdata(file);
struct camera_data *cam = video_get_drvdata(dev);
- struct cpia2_fh *fh = file->private_data;
- if (cam->present &&
- (cam->open_count == 1 || fh->prio == V4L2_PRIORITY_RECORD)) {
+ if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) {
cpia2_usb_stream_stop(cam);
- if (cam->open_count == 1) {
- /* save camera state for later open */
- cpia2_save_camera_state(cam);
+ /* save camera state for later open */
+ cpia2_save_camera_state(cam);
- cpia2_set_low_power(cam);
- cpia2_free_buffers(cam);
- }
+ cpia2_set_low_power(cam);
+ cpia2_free_buffers(cam);
}
- if (fh->mmapped)
+ if (cam->stream_fh == file->private_data) {
+ cam->stream_fh = NULL;
cam->mmapped = 0;
- v4l2_prio_close(&cam->prio, fh->prio);
- file->private_data = NULL;
- kfree(fh);
-
- if (--cam->open_count == 0) {
- cpia2_free_buffers(cam);
- if (!cam->present) {
- video_unregister_device(dev);
- kfree(cam);
- return 0;
- }
}
-
- return 0;
+ return v4l2_fh_release(file);
}
/******************************************************************************
@@ -327,16 +147,9 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count,
struct camera_data *cam = video_drvdata(file);
int noblock = file->f_flags&O_NONBLOCK;
- struct cpia2_fh *fh = file->private_data;
-
if(!cam)
return -EINVAL;
- /* Priority check */
- if(fh->prio != V4L2_PRIORITY_RECORD) {
- return -EBUSY;
- }
-
return cpia2_read(cam, buf, count, noblock);
}
@@ -349,15 +162,6 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count,
static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait)
{
struct camera_data *cam = video_drvdata(filp);
- struct cpia2_fh *fh = filp->private_data;
-
- if(!cam)
- return POLLERR;
-
- /* Priority check */
- if(fh->prio != V4L2_PRIORITY_RECORD) {
- return POLLERR;
- }
return cpia2_poll(cam, filp, wait);
}
@@ -384,36 +188,13 @@ static int sync(struct camera_data *cam, int frame_nr)
mutex_lock(&cam->v4l2_lock);
if (signal_pending(current))
return -ERESTARTSYS;
- if(!cam->present)
+ if (!video_is_registered(&cam->vdev))
return -ENOTTY;
}
}
/******************************************************************************
*
- * ioctl_set_gpio
- *
- *****************************************************************************/
-
-static long cpia2_default(struct file *file, void *fh, bool valid_prio,
- int cmd, void *arg)
-{
- struct camera_data *cam = video_drvdata(file);
- __u32 gpio_val;
-
- if (cmd != CPIA2_CID_GPIO)
- return -EINVAL;
-
- gpio_val = *(__u32*) arg;
-
- if (gpio_val &~ 0xFFU)
- return -EINVAL;
-
- return cpia2_set_gpio(cam, (unsigned char)gpio_val);
-}
-
-/******************************************************************************
- *
* ioctl_querycap
*
* V4L2 device capabilities
@@ -465,9 +246,11 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v
if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0)
memset(vc->bus_info,0, sizeof(vc->bus_info));
- vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ vc->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING;
+ vc->capabilities = vc->device_caps |
+ V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -610,22 +393,12 @@ static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh,
struct v4l2_format *f)
{
struct camera_data *cam = video_drvdata(file);
- struct cpia2_fh *fh = _fh;
int err, frame;
- err = v4l2_prio_check(&cam->prio, fh->prio);
- if (err)
- return err;
err = cpia2_try_fmt_vid_cap(file, _fh, f);
if(err != 0)
return err;
- /* Ensure that only this process can change the format. */
- err = v4l2_prio_change(&cam->prio, &fh->prio, V4L2_PRIORITY_RECORD);
- if(err != 0) {
- return err;
- }
-
cam->pixelformat = f->fmt.pix.pixelformat;
/* NOTE: This should be set to 1 for MJPEG, but some apps don't handle
@@ -713,240 +486,126 @@ static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c)
return 0;
}
-/******************************************************************************
- *
- * ioctl_queryctrl
- *
- * V4L2 query possible control variables
- *
- *****************************************************************************/
+struct framerate_info {
+ int value;
+ struct v4l2_fract period;
+};
-static int cpia2_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+static const struct framerate_info framerate_controls[] = {
+ { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } },
+ { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } },
+ { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } },
+ { CPIA2_VP_FRAMERATE_15, { 1, 15 } },
+ { CPIA2_VP_FRAMERATE_25, { 1, 25 } },
+ { CPIA2_VP_FRAMERATE_30, { 1, 30 } },
+};
+
+static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p)
{
struct camera_data *cam = video_drvdata(file);
+ struct v4l2_captureparm *cap = &p->parm.capture;
int i;
- for(i=0; i<NUM_CONTROLS; ++i) {
- if(c->id == controls[i].id) {
- memcpy(c, controls+i, sizeof(*c));
- break;
- }
- }
-
- if(i == NUM_CONTROLS)
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- /* Some devices have additional limitations */
- switch(c->id) {
- case V4L2_CID_BRIGHTNESS:
- /***
- * Don't let the register be set to zero - bug in VP4
- * flash of full brightness
- ***/
- if (cam->params.pnp_id.device_type == DEVICE_STV_672)
- c->minimum = 1;
- break;
- case V4L2_CID_VFLIP:
- // VP5 Only
- if(cam->params.pnp_id.device_type == DEVICE_STV_672)
- c->flags |= V4L2_CTRL_FLAG_DISABLED;
- break;
- case CPIA2_CID_FRAMERATE:
- if(cam->params.pnp_id.device_type == DEVICE_STV_672 &&
- cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){
- // Maximum 15fps
- for(i=0; i<c->maximum; ++i) {
- if(framerate_controls[i].value ==
- CPIA2_VP_FRAMERATE_15) {
- c->maximum = i;
- c->default_value = i;
- }
- }
+ cap->capability = V4L2_CAP_TIMEPERFRAME;
+ cap->readbuffers = cam->num_frames;
+ for (i = 0; i < ARRAY_SIZE(framerate_controls); i++)
+ if (cam->params.vp_params.frame_rate == framerate_controls[i].value) {
+ cap->timeperframe = framerate_controls[i].period;
+ break;
}
- break;
- case CPIA2_CID_FLICKER_MODE:
- // Flicker control only valid for 672.
- if(cam->params.pnp_id.device_type != DEVICE_STV_672)
- c->flags |= V4L2_CTRL_FLAG_DISABLED;
- break;
- case CPIA2_CID_LIGHTS:
- // Light control only valid for the QX5 Microscope.
- if(cam->params.pnp_id.product != 0x151)
- c->flags |= V4L2_CTRL_FLAG_DISABLED;
- break;
- default:
- break;
- }
-
return 0;
}
-/******************************************************************************
- *
- * ioctl_querymenu
- *
- * V4L2 query possible control variables
- *
- *****************************************************************************/
-
-static int cpia2_querymenu(struct file *file, void *fh, struct v4l2_querymenu *m)
+static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p)
{
struct camera_data *cam = video_drvdata(file);
+ struct v4l2_captureparm *cap = &p->parm.capture;
+ struct v4l2_fract tpf = cap->timeperframe;
+ int max = ARRAY_SIZE(framerate_controls) - 1;
+ int ret;
+ int i;
- switch(m->id) {
- case CPIA2_CID_FLICKER_MODE:
- if (m->index >= NUM_FLICKER_CONTROLS)
- return -EINVAL;
+ ret = cpia2_g_parm(file, fh, p);
+ if (ret || !tpf.denominator || !tpf.numerator)
+ return ret;
+
+ /* Maximum 15 fps for this model */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
+ max -= 2;
+ for (i = 0; i <= max; i++) {
+ struct v4l2_fract f1 = tpf;
+ struct v4l2_fract f2 = framerate_controls[i].period;
+
+ f1.numerator *= f2.denominator;
+ f2.numerator *= f1.denominator;
+ if (f1.numerator >= f2.numerator)
+ break;
+ }
+ if (i > max)
+ i = max;
+ cap->timeperframe = framerate_controls[i].period;
+ return cpia2_set_fps(cam, framerate_controls[i].value);
+}
- strcpy(m->name, flicker_controls[m->index].name);
- break;
- case CPIA2_CID_FRAMERATE:
- {
- int maximum = NUM_FRAMERATE_CONTROLS - 1;
- if(cam->params.pnp_id.device_type == DEVICE_STV_672 &&
- cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){
- // Maximum 15fps
- int i;
- for(i=0; i<maximum; ++i) {
- if(framerate_controls[i].value ==
- CPIA2_VP_FRAMERATE_15)
- maximum = i;
- }
- }
- if (m->index > maximum)
- return -EINVAL;
+static const struct {
+ u32 width;
+ u32 height;
+} cpia2_framesizes[] = {
+ { 640, 480 },
+ { 352, 288 },
+ { 320, 240 },
+ { 288, 216 },
+ { 256, 192 },
+ { 224, 168 },
+ { 192, 144 },
+ { 176, 144 },
+};
- strcpy(m->name, framerate_controls[m->index].name);
- break;
- }
- case CPIA2_CID_LIGHTS:
- if (m->index >= NUM_LIGHTS_CONTROLS)
- return -EINVAL;
+static int cpia2_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
- strcpy(m->name, lights_controls[m->index].name);
- break;
- default:
+ if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG &&
+ fsize->pixel_format != V4L2_PIX_FMT_JPEG)
return -EINVAL;
- }
+ if (fsize->index >= ARRAY_SIZE(cpia2_framesizes))
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = cpia2_framesizes[fsize->index].width;
+ fsize->discrete.height = cpia2_framesizes[fsize->index].height;
return 0;
}
-/******************************************************************************
- *
- * ioctl_g_ctrl
- *
- * V4L2 get the value of a control variable
- *
- *****************************************************************************/
-
-static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+static int cpia2_enum_frameintervals(struct file *file, void *fh,
+ struct v4l2_frmivalenum *fival)
{
struct camera_data *cam = video_drvdata(file);
+ int max = ARRAY_SIZE(framerate_controls) - 1;
+ int i;
- switch(c->id) {
- case V4L2_CID_BRIGHTNESS:
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS,
- TRANSFER_READ, 0);
- c->value = cam->params.color_params.brightness;
- break;
- case V4L2_CID_CONTRAST:
- cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST,
- TRANSFER_READ, 0);
- c->value = cam->params.color_params.contrast;
- break;
- case V4L2_CID_SATURATION:
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION,
- TRANSFER_READ, 0);
- c->value = cam->params.color_params.saturation;
- break;
- case V4L2_CID_HFLIP:
- cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS,
- TRANSFER_READ, 0);
- c->value = (cam->params.vp_params.user_effects &
- CPIA2_VP_USER_EFFECTS_MIRROR) != 0;
- break;
- case V4L2_CID_VFLIP:
- cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS,
- TRANSFER_READ, 0);
- c->value = (cam->params.vp_params.user_effects &
- CPIA2_VP_USER_EFFECTS_FLIP) != 0;
- break;
- case CPIA2_CID_TARGET_KB:
- c->value = cam->params.vc_params.target_kb;
- break;
- case CPIA2_CID_GPIO:
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA,
- TRANSFER_READ, 0);
- c->value = cam->params.vp_params.gpio_data;
- break;
- case CPIA2_CID_FLICKER_MODE:
- {
- int i, mode;
- cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES,
- TRANSFER_READ, 0);
- if(cam->params.flicker_control.cam_register &
- CPIA2_VP_FLICKER_MODES_NEVER_FLICKER) {
- mode = NEVER_FLICKER;
- } else {
- if(cam->params.flicker_control.cam_register &
- CPIA2_VP_FLICKER_MODES_50HZ) {
- mode = FLICKER_50;
- } else {
- mode = FLICKER_60;
- }
- }
- for(i=0; i<NUM_FLICKER_CONTROLS; i++) {
- if(flicker_controls[i].value == mode) {
- c->value = i;
- break;
- }
- }
- if(i == NUM_FLICKER_CONTROLS)
- return -EINVAL;
- break;
- }
- case CPIA2_CID_FRAMERATE:
- {
- int maximum = NUM_FRAMERATE_CONTROLS - 1;
- int i;
- for(i=0; i<= maximum; i++) {
- if(cam->params.vp_params.frame_rate ==
- framerate_controls[i].value)
- break;
- }
- if(i > maximum)
- return -EINVAL;
- c->value = i;
- break;
- }
- case CPIA2_CID_USB_ALT:
- c->value = cam->params.camera_state.stream_mode;
- break;
- case CPIA2_CID_LIGHTS:
- {
- int i;
- cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA,
- TRANSFER_READ, 0);
- for(i=0; i<NUM_LIGHTS_CONTROLS; i++) {
- if((cam->params.vp_params.gpio_data&GPIO_LIGHTS_MASK) ==
- lights_controls[i].value) {
- break;
- }
- }
- if(i == NUM_LIGHTS_CONTROLS)
- return -EINVAL;
- c->value = i;
- break;
- }
- case CPIA2_CID_RESET_CAMERA:
+ if (fival->pixel_format != V4L2_PIX_FMT_MJPEG &&
+ fival->pixel_format != V4L2_PIX_FMT_JPEG)
return -EINVAL;
- default:
- return -EINVAL;
- }
-
- DBG("Get control id:%d, value:%d\n", c->id, c->value);
+ /* Maximum 15 fps for this model */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672 &&
+ cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500)
+ max -= 2;
+ if (fival->index > max)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++)
+ if (fival->width == cpia2_framesizes[i].width &&
+ fival->height == cpia2_framesizes[i].height)
+ break;
+ if (i == ARRAY_SIZE(cpia2_framesizes))
+ return -EINVAL;
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = framerate_controls[fival->index].period;
return 0;
}
@@ -958,72 +617,54 @@ static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
*
*****************************************************************************/
-static int cpia2_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct camera_data *cam = video_drvdata(file);
- int i;
- int retval = 0;
+ struct camera_data *cam =
+ container_of(ctrl->handler, struct camera_data, hdl);
+ static const int flicker_table[] = {
+ NEVER_FLICKER,
+ FLICKER_50,
+ FLICKER_60,
+ };
- DBG("Set control id:%d, value:%d\n", c->id, c->value);
-
- /* Check that the value is in range */
- for(i=0; i<NUM_CONTROLS; i++) {
- if(c->id == controls[i].id) {
- if(c->value < controls[i].minimum ||
- c->value > controls[i].maximum) {
- return -EINVAL;
- }
- break;
- }
- }
- if(i == NUM_CONTROLS)
- return -EINVAL;
+ DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val);
- switch(c->id) {
+ switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- cpia2_set_brightness(cam, c->value);
+ cpia2_set_brightness(cam, ctrl->val);
break;
case V4L2_CID_CONTRAST:
- cpia2_set_contrast(cam, c->value);
+ cpia2_set_contrast(cam, ctrl->val);
break;
case V4L2_CID_SATURATION:
- cpia2_set_saturation(cam, c->value);
+ cpia2_set_saturation(cam, ctrl->val);
break;
case V4L2_CID_HFLIP:
- cpia2_set_property_mirror(cam, c->value);
+ cpia2_set_property_mirror(cam, ctrl->val);
break;
case V4L2_CID_VFLIP:
- cpia2_set_property_flip(cam, c->value);
+ cpia2_set_property_flip(cam, ctrl->val);
break;
- case CPIA2_CID_TARGET_KB:
- retval = cpia2_set_target_kb(cam, c->value);
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]);
+ case V4L2_CID_ILLUMINATORS_1:
+ return cpia2_set_gpio(cam, (cam->top_light->val << 6) |
+ (cam->bottom_light->val << 7));
+ case V4L2_CID_JPEG_ACTIVE_MARKER:
+ cam->params.compression.inhibit_htables =
+ !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT);
break;
- case CPIA2_CID_GPIO:
- retval = cpia2_set_gpio(cam, c->value);
- break;
- case CPIA2_CID_FLICKER_MODE:
- retval = cpia2_set_flicker_mode(cam,
- flicker_controls[c->value].value);
- break;
- case CPIA2_CID_FRAMERATE:
- retval = cpia2_set_fps(cam, framerate_controls[c->value].value);
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ cam->params.vc_params.quality = ctrl->val;
break;
case CPIA2_CID_USB_ALT:
- retval = cpia2_usb_change_streaming_alternate(cam, c->value);
- break;
- case CPIA2_CID_LIGHTS:
- retval = cpia2_set_gpio(cam, lights_controls[c->value].value);
- break;
- case CPIA2_CID_RESET_CAMERA:
- cpia2_usb_stream_pause(cam);
- cpia2_reset_camera(cam);
- cpia2_usb_stream_resume(cam);
+ cam->params.camera_state.stream_mode = ctrl->val;
break;
default:
- retval = -EINVAL;
+ return -EINVAL;
}
- return retval;
+ return 0;
}
/******************************************************************************
@@ -1084,6 +725,8 @@ static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompres
cam->params.compression.inhibit_htables =
!(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT);
+ parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI |
+ V4L2_JPEG_MARKER_DHT;
if(parms->APP_len != 0) {
if(parms->APP_len > 0 &&
@@ -1270,12 +913,12 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
struct framebuf *cb=cam->curbuff;
mutex_unlock(&cam->v4l2_lock);
wait_event_interruptible(cam->wq_stream,
- !cam->present ||
+ !video_is_registered(&cam->vdev) ||
(cb=cam->curbuff)->status == FRAME_READY);
mutex_lock(&cam->v4l2_lock);
if (signal_pending(current))
return -ERESTARTSYS;
- if(!cam->present)
+ if (!video_is_registered(&cam->vdev))
return -ENOTTY;
frame = cb->num;
}
@@ -1299,56 +942,39 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
return 0;
}
-static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p)
-{
- struct cpia2_fh *fh = _fh;
-
- *p = fh->prio;
- return 0;
-}
-
-static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio)
-{
- struct camera_data *cam = video_drvdata(file);
- struct cpia2_fh *fh = _fh;
-
- if (cam->streaming && prio != fh->prio &&
- fh->prio == V4L2_PRIORITY_RECORD)
- /* Can't drop record priority while streaming */
- return -EBUSY;
-
- if (prio == V4L2_PRIORITY_RECORD && prio != fh->prio &&
- v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD)
- /* Only one program can record at a time */
- return -EBUSY;
- return v4l2_prio_change(&cam->prio, &fh->prio, prio);
-}
-
static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
{
struct camera_data *cam = video_drvdata(file);
+ int ret = -EINVAL;
DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming);
if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- if (!cam->streaming)
- return cpia2_usb_stream_start(cam,
+ if (!cam->streaming) {
+ ret = cpia2_usb_stream_start(cam,
cam->params.camera_state.stream_mode);
- return -EINVAL;
+ if (!ret)
+ v4l2_ctrl_grab(cam->usb_alt, true);
+ }
+ return ret;
}
static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
{
struct camera_data *cam = video_drvdata(file);
+ int ret = -EINVAL;
DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming);
if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- if (cam->streaming)
- return cpia2_usb_stream_stop(cam);
- return -EINVAL;
+ if (cam->streaming) {
+ ret = cpia2_usb_stream_stop(cam);
+ if (!ret)
+ v4l2_ctrl_grab(cam->usb_alt, false);
+ }
+ return ret;
}
/******************************************************************************
@@ -1361,16 +987,10 @@ static int cpia2_mmap(struct file *file, struct vm_area_struct *area)
struct camera_data *cam = video_drvdata(file);
int retval;
- /* Priority check */
- struct cpia2_fh *fh = file->private_data;
- if(fh->prio != V4L2_PRIORITY_RECORD) {
- return -EBUSY;
- }
-
retval = cpia2_remap_buffer(cam, area);
if(!retval)
- fh->mmapped = 1;
+ cam->stream_fh = file->private_data;
return retval;
}
@@ -1388,15 +1008,13 @@ static void reset_camera_struct_v4l(struct camera_data *cam)
cam->frame_size = buffer_size;
cam->num_frames = num_buffers;
- /* FlickerModes */
+ /* Flicker modes */
cam->params.flicker_control.flicker_mode_req = flicker_mode;
- cam->params.flicker_control.mains_frequency = flicker_freq;
- /* streamMode */
+ /* stream modes */
cam->params.camera_state.stream_mode = alternate;
cam->pixelformat = V4L2_PIX_FMT_JPEG;
- v4l2_prio_init(&cam->prio);
}
static const struct v4l2_ioctl_ops cpia2_ioctl_ops = {
@@ -1408,10 +1026,6 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = {
.vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap,
- .vidioc_queryctrl = cpia2_queryctrl,
- .vidioc_querymenu = cpia2_querymenu,
- .vidioc_g_ctrl = cpia2_g_ctrl,
- .vidioc_s_ctrl = cpia2_s_ctrl,
.vidioc_g_jpegcomp = cpia2_g_jpegcomp,
.vidioc_s_jpegcomp = cpia2_s_jpegcomp,
.vidioc_cropcap = cpia2_cropcap,
@@ -1421,9 +1035,12 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = {
.vidioc_dqbuf = cpia2_dqbuf,
.vidioc_streamon = cpia2_streamon,
.vidioc_streamoff = cpia2_streamoff,
- .vidioc_g_priority = cpia2_g_priority,
- .vidioc_s_priority = cpia2_s_priority,
- .vidioc_default = cpia2_default,
+ .vidioc_s_parm = cpia2_s_parm,
+ .vidioc_g_parm = cpia2_g_parm,
+ .vidioc_enum_framesizes = cpia2_enum_framesizes,
+ .vidioc_enum_frameintervals = cpia2_enum_frameintervals,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/***
@@ -1444,7 +1061,21 @@ static struct video_device cpia2_template = {
.name = "CPiA2 Camera",
.fops = &cpia2_fops,
.ioctl_ops = &cpia2_ioctl_ops,
- .release = video_device_release,
+ .release = video_device_release_empty,
+};
+
+void cpia2_camera_release(struct v4l2_device *v4l2_dev)
+{
+ struct camera_data *cam =
+ container_of(v4l2_dev, struct camera_data, v4l2_dev);
+
+ v4l2_ctrl_handler_free(&cam->hdl);
+ v4l2_device_unregister(&cam->v4l2_dev);
+ kfree(cam);
+}
+
+static const struct v4l2_ctrl_ops cpia2_ctrl_ops = {
+ .s_ctrl = cpia2_s_ctrl,
};
/******************************************************************************
@@ -1454,20 +1085,78 @@ static struct video_device cpia2_template = {
*****************************************************************************/
int cpia2_register_camera(struct camera_data *cam)
{
- cam->vdev = video_device_alloc();
- if(!cam->vdev)
- return -ENOMEM;
+ struct v4l2_ctrl_handler *hdl = &cam->hdl;
+ struct v4l2_ctrl_config cpia2_usb_alt = {
+ .ops = &cpia2_ctrl_ops,
+ .id = CPIA2_CID_USB_ALT,
+ .name = "USB Alternate",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = USBIF_ISO_1,
+ .max = USBIF_ISO_6,
+ .step = 1,
+ };
+ int ret;
+
+ v4l2_ctrl_handler_init(hdl, 12);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_BRIGHTNESS,
+ cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0,
+ 255, 1, DEFAULT_BRIGHTNESS);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_JPEG_ACTIVE_MARKER, 0,
+ V4L2_JPEG_ACTIVE_MARKER_DHT, 0,
+ V4L2_JPEG_ACTIVE_MARKER_DHT);
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 1,
+ 100, 1, 100);
+ cpia2_usb_alt.def = alternate;
+ cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL);
+ /* VP5 Only */
+ if (cam->params.pnp_id.device_type != DEVICE_STV_672)
+ v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ /* Flicker control only valid for 672 */
+ if (cam->params.pnp_id.device_type == DEVICE_STV_672)
+ v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0);
+ /* Light control only valid for the QX5 Microscope */
+ if (cam->params.pnp_id.product == 0x151) {
+ cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);
+ cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &cam->top_light);
+ }
- memcpy(cam->vdev, &cpia2_template, sizeof(cpia2_template));
- video_set_drvdata(cam->vdev, cam);
- cam->vdev->lock = &cam->v4l2_lock;
+ if (hdl->error) {
+ ret = hdl->error;
+ v4l2_ctrl_handler_free(hdl);
+ return ret;
+ }
+
+ cam->vdev = cpia2_template;
+ video_set_drvdata(&cam->vdev, cam);
+ cam->vdev.lock = &cam->v4l2_lock;
+ cam->vdev.ctrl_handler = hdl;
+ cam->vdev.v4l2_dev = &cam->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags);
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &cam->vdev.flags);
reset_camera_struct_v4l(cam);
/* register v4l device */
- if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+ if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
ERR("video_register_device failed\n");
- video_device_release(cam->vdev);
return -ENODEV;
}
@@ -1481,13 +1170,7 @@ int cpia2_register_camera(struct camera_data *cam)
*****************************************************************************/
void cpia2_unregister_camera(struct camera_data *cam)
{
- if (!cam->open_count) {
- video_unregister_device(cam->vdev);
- } else {
- LOG("%s removed while open, deferring "
- "video_unregister_device\n",
- video_device_node_name(cam->vdev));
- }
+ video_unregister_device(&cam->vdev);
}
/******************************************************************************
@@ -1524,23 +1207,12 @@ static void __init check_parameters(void)
LOG("alternate specified is invalid, using %d\n", alternate);
}
- if (flicker_mode != NEVER_FLICKER && flicker_mode != ANTI_FLICKER_ON) {
- flicker_mode = NEVER_FLICKER;
+ if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) {
+ flicker_mode = 0;
LOG("Flicker mode specified is invalid, using %d\n",
flicker_mode);
}
- if (flicker_freq != FLICKER_50 && flicker_freq != FLICKER_60) {
- flicker_freq = FLICKER_60;
- LOG("Flicker mode specified is invalid, using %d\n",
- flicker_freq);
- }
-
- if(video_nr < -1 || video_nr > 64) {
- video_nr = -1;
- LOG("invalid video_nr specified, must be -1 to 64\n");
- }
-
DBG("Using %d buffers, each %d bytes, alternate=%d\n",
num_buffers, buffer_size, alternate);
}
diff --git a/drivers/media/video/cpia2/cpia2dev.h b/drivers/media/video/cpia2/cpia2dev.h
deleted file mode 100644
index f66691fe5a3..00000000000
--- a/drivers/media/video/cpia2/cpia2dev.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/****************************************************************************
- *
- * Filename: cpia2dev.h
- *
- * Copyright 2001, STMicrolectronics, Inc.
- *
- * Contact: steve.miller@st.com
- *
- * Description:
- * This file provides definitions for applications wanting to use the
- * cpia2 driver beyond the generic v4l capabilities.
- *
- * 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 CPIA2_DEV_HEADER
-#define CPIA2_DEV_HEADER
-
-#include <linux/videodev2.h>
-
-/***
- * The following defines are ioctl numbers based on video4linux private ioctls,
- * which can range from 192 (BASE_VIDIOCPRIVATE) to 255. All of these take int
- * args
- */
-#define CPIA2_IOC_SET_GPIO _IOW('v', BASE_VIDIOC_PRIVATE + 17, __u32)
-
-/* V4L2 driver specific controls */
-#define CPIA2_CID_TARGET_KB (V4L2_CID_PRIVATE_BASE+0)
-#define CPIA2_CID_GPIO (V4L2_CID_PRIVATE_BASE+1)
-#define CPIA2_CID_FLICKER_MODE (V4L2_CID_PRIVATE_BASE+2)
-#define CPIA2_CID_FRAMERATE (V4L2_CID_PRIVATE_BASE+3)
-#define CPIA2_CID_USB_ALT (V4L2_CID_PRIVATE_BASE+4)
-#define CPIA2_CID_LIGHTS (V4L2_CID_PRIVATE_BASE+5)
-#define CPIA2_CID_RESET_CAMERA (V4L2_CID_PRIVATE_BASE+6)
-
-#endif
diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c
index e118361c2e7..6d2a98246b6 100644
--- a/drivers/media/video/cx18/cx18-alsa-main.c
+++ b/drivers/media/video/cx18/cx18-alsa-main.c
@@ -285,6 +285,7 @@ static void __exit cx18_alsa_exit(void)
drv = driver_find("cx18", &pci_bus_type);
ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
+ (void)ret; /* suppress compiler warning */
cx18_ext_init = NULL;
printk(KERN_INFO "cx18-alsa: module unload complete\n");
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c
index 82d195be919..7a5b84a86bb 100644
--- a/drivers/media/video/cx18/cx18-alsa-pcm.c
+++ b/drivers/media/video/cx18/cx18-alsa-pcm.c
@@ -190,7 +190,7 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream)
ret = cx18_start_v4l2_encode_stream(s);
snd_cx18_unlock(cxsc);
- return 0;
+ return ret;
}
static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream)
@@ -199,12 +199,11 @@ static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream)
struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
struct cx18 *cx = to_cx18(v4l2_dev);
struct cx18_stream *s;
- int ret;
/* Instruct the cx18 to stop sending packets */
snd_cx18_lock(cxsc);
s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
- ret = cx18_stop_v4l2_encode_stream(s, 0);
+ cx18_stop_v4l2_encode_stream(s, 0);
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
cx18_release_stream(s);
@@ -252,13 +251,10 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- int ret;
-
dprintk("%s called\n", __func__);
- ret = snd_pcm_alloc_vmalloc_buffer(substream,
+ return snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(params));
- return 0;
}
static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream)
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index be49f68ddf3..35fde4e931f 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -1137,7 +1137,7 @@ static long cx18_default(struct file *file, void *fh, bool valid_prio,
}
default:
- return -EINVAL;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index 0c7796e76ac..ed8118390b0 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -595,9 +595,8 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
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, irq, req, ack, err;
+ u32 irq, req, ack, err;
struct cx18_mailbox __iomem *mb;
- u32 __iomem *xpu_state;
wait_queue_head_t *waitq;
struct mutex *mb_lock;
unsigned long int t0, timeout, ret;
@@ -628,14 +627,12 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
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);
@@ -653,7 +650,6 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
* 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,
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 638cca156b5..4185bcb80ca 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -980,7 +980,6 @@ void cx18_stop_all_captures(struct cx18 *cx)
int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
{
struct cx18 *cx = s->cx;
- unsigned long then;
if (!cx18_stream_enabled(s))
return -EINVAL;
@@ -999,8 +998,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
else
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
- then = jiffies;
-
if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
}
diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c
index d4327dab5a3..ce2f62238a1 100644
--- a/drivers/media/video/cx231xx/cx231xx-417.c
+++ b/drivers/media/video/cx231xx/cx231xx-417.c
@@ -1095,7 +1095,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev)
{
int version;
int retval;
- u32 i, data[7];
+ u32 i;
u32 val = 0;
dprintk(1, "%s()\n", __func__);
@@ -1154,6 +1154,11 @@ static int cx231xx_initialize_codec(struct cx231xx *dev)
CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0);
*/
+
+#if 0
+ /* TODO */
+ u32 data[7];
+
/* Setup to capture VBI */
data[0] = 0x0001BD00;
data[1] = 1; /* frames per interrupt */
@@ -1162,7 +1167,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev)
data[4] = 0x206080C0; /* stop codes */
data[5] = 6; /* lines */
data[6] = 64; /* BPL */
-/*
+
cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1],
data[2], data[3], data[4], data[5], data[6]);
@@ -1175,7 +1180,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev)
cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0,
i | 0x80000000, valid, 0, 0, 0);
}
-*/
+#endif
/* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE);
msleep(60);
*/
@@ -1792,17 +1797,16 @@ static int vidioc_streamon(struct file *file, void *priv,
struct cx231xx_fh *fh = file->private_data;
struct cx231xx *dev = fh->dev;
- int rc = 0;
dprintk(3, "enter vidioc_streamon()\n");
cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
- rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
+ cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
if (dev->USE_ISO)
- rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
+ cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
CX231XX_NUM_BUFS,
dev->video_mode.max_pkt_size,
cx231xx_isoc_copy);
else {
- rc = cx231xx_init_bulk(dev, 320,
+ cx231xx_init_bulk(dev, 320,
5,
dev->ts1_mode.max_pkt_size,
cx231xx_bulk_copy);
diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c
index a2c2b7d343e..068f78dc5d1 100644
--- a/drivers/media/video/cx231xx/cx231xx-audio.c
+++ b/drivers/media/video/cx231xx/cx231xx-audio.c
@@ -523,21 +523,24 @@ static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- unsigned int channels, rate, format;
int ret;
dprintk("Setting capture parameters\n");
ret = snd_pcm_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
+#if 0
+ /* TODO: set up cx231xx audio chip to deliver the correct audio format,
+ current default is 48000hz multiplexed => 96000hz mono
+ which shouldn't matter since analogue TV only supports mono */
+ unsigned int channels, rate, format;
+
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
+#endif
- /* TODO: set up cx231xx audio chip to deliver the correct audio format,
- current default is 48000hz multiplexed => 96000hz mono
- which shouldn't matter since analogue TV only supports mono */
- return 0;
+ return ret;
}
static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
@@ -586,7 +589,7 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct cx231xx *dev = snd_pcm_substream_chip(substream);
- int retval;
+ int retval = 0;
if (dev->state & DEV_DISCONNECTED)
return -ENODEV;
@@ -601,12 +604,13 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
break;
default:
retval = -EINVAL;
+ break;
}
spin_unlock(&dev->adev.slock);
schedule_work(&dev->wq_trigger);
- return 0;
+ return retval;
}
static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
index 53ff26e7abf..b085a3c6dc0 100644
--- a/drivers/media/video/cx231xx/cx231xx-avcore.c
+++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
@@ -934,33 +934,29 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev,
void cx231xx_enable656(struct cx231xx *dev)
{
u8 temp = 0;
- int status;
/*enable TS1 data[0:7] as output to export 656*/
- status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF);
+ vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF);
/*enable TS1 clock as output to export 656*/
- status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
+ vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
temp = temp|0x04;
- status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
-
+ vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
}
EXPORT_SYMBOL_GPL(cx231xx_enable656);
void cx231xx_disable656(struct cx231xx *dev)
{
u8 temp = 0;
- int status;
-
- status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00);
+ vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00);
- status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
+ vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp);
temp = temp&0xFB;
- status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
+ vid_blk_write_byte(dev, TS1_PIN_CTL1, temp);
}
EXPORT_SYMBOL_GPL(cx231xx_disable656);
@@ -1320,117 +1316,115 @@ void update_HH_register_after_set_DIF(struct cx231xx *dev)
void cx231xx_dump_HH_reg(struct cx231xx *dev)
{
- u8 status = 0;
u32 value = 0;
u16 i = 0;
value = 0x45005390;
- status = vid_blk_write_word(dev, 0x104, value);
+ vid_blk_write_word(dev, 0x104, value);
for (i = 0x100; i < 0x140; i++) {
- status = vid_blk_read_word(dev, i, &value);
+ vid_blk_read_word(dev, i, &value);
cx231xx_info("reg0x%x=0x%x\n", i, value);
i = i+3;
}
for (i = 0x300; i < 0x400; i++) {
- status = vid_blk_read_word(dev, i, &value);
+ vid_blk_read_word(dev, i, &value);
cx231xx_info("reg0x%x=0x%x\n", i, value);
i = i+3;
}
for (i = 0x400; i < 0x440; i++) {
- status = vid_blk_read_word(dev, i, &value);
+ vid_blk_read_word(dev, i, &value);
cx231xx_info("reg0x%x=0x%x\n", i, value);
i = i+3;
}
- status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+ vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390);
- status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
+ vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value);
cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value);
}
void cx231xx_dump_SC_reg(struct cx231xx *dev)
{
u8 value[4] = { 0, 0, 0, 0 };
- int status = 0;
cx231xx_info("cx231xx_dump_SC_reg!\n");
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0],
value[1], value[2], value[3]);
- status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
+ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
value, 4);
cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0],
value[1], value[2], value[3]);
@@ -1441,18 +1435,15 @@ void cx231xx_dump_SC_reg(struct cx231xx *dev)
void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev)
{
- u8 status = 0;
u8 value = 0;
-
-
- status = afe_read_byte(dev, ADC_STATUS2_CH3, &value);
+ afe_read_byte(dev, ADC_STATUS2_CH3, &value);
value = (value & 0xFE)|0x01;
- status = afe_write_byte(dev, ADC_STATUS2_CH3, value);
+ afe_write_byte(dev, ADC_STATUS2_CH3, value);
- status = afe_read_byte(dev, ADC_STATUS2_CH3, &value);
+ afe_read_byte(dev, ADC_STATUS2_CH3, &value);
value = (value & 0xFE)|0x00;
- status = afe_write_byte(dev, ADC_STATUS2_CH3, value);
+ afe_write_byte(dev, ADC_STATUS2_CH3, value);
/*
@@ -1464,44 +1455,43 @@ void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev)
for low-if agc defect
*/
- status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value);
+ afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value);
value = (value & 0xFC)|0x00;
- status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value);
+ afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value);
- status = afe_read_byte(dev, ADC_INPUT_CH3, &value);
+ afe_read_byte(dev, ADC_INPUT_CH3, &value);
value = (value & 0xF9)|0x02;
- status = afe_write_byte(dev, ADC_INPUT_CH3, value);
+ afe_write_byte(dev, ADC_INPUT_CH3, value);
- status = afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value);
+ afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value);
value = (value & 0xFB)|0x04;
- status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, value);
+ afe_write_byte(dev, ADC_FB_FRCRST_CH3, value);
- status = afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value);
+ afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value);
value = (value & 0xFC)|0x03;
- status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value);
+ afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value);
- status = afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value);
+ afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value);
value = (value & 0xFB)|0x04;
- status = afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value);
+ afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value);
- status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
+ afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
value = (value & 0xF8)|0x06;
- status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
+ afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
- status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
+ afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value);
value = (value & 0x8F)|0x40;
- status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
+ afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value);
- status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value);
+ afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value);
value = (value & 0xDF)|0x20;
- status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value);
+ afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value);
}
void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq,
u8 spectral_invert, u32 mode)
{
u32 colibri_carrier_offset = 0;
- u8 status = 0;
u32 func_mode = 0x01; /* Device has a DIF if this function is called */
u32 standard = 0;
u8 value[4] = { 0, 0, 0, 0 };
@@ -1511,15 +1501,15 @@ void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq,
value[1] = (u8) 0x6F;
value[2] = (u8) 0x6F;
value[3] = (u8) 0x6F;
- status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
PWR_CTL_EN, value, 4);
/*Set colibri for low IF*/
- status = cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF);
+ cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF);
/* Set C2HH for low IF operation.*/
standard = dev->norm;
- status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
+ cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
func_mode, standard);
/* Get colibri offsets.*/
@@ -1556,7 +1546,6 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
u8 spectral_invert, u32 mode)
{
unsigned long pll_freq_word;
- int status = 0;
u32 dif_misc_ctrl_value = 0;
u64 pll_freq_u64 = 0;
u32 i = 0;
@@ -1567,7 +1556,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
if (mode == TUNER_MODE_FM_RADIO) {
pll_freq_word = 0x905A1CAC;
- status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word);
+ vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word);
} else /*KSPROPERTY_TUNER_MODE_TV*/{
/* Calculate the PLL frequency word based on the adjusted if_freq*/
@@ -1576,23 +1565,23 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
do_div(pll_freq_u64, 50000000);
pll_freq_word = (u32)pll_freq_u64;
/*pll_freq_word = 0x3463497;*/
- status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word);
+ vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word);
if (spectral_invert) {
if_freq -= 400000;
/* Enable Spectral Invert*/
- status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+ vid_blk_read_word(dev, DIF_MISC_CTRL,
&dif_misc_ctrl_value);
dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000;
- status = vid_blk_write_word(dev, DIF_MISC_CTRL,
+ vid_blk_write_word(dev, DIF_MISC_CTRL,
dif_misc_ctrl_value);
} else {
if_freq += 400000;
/* Disable Spectral Invert*/
- status = vid_blk_read_word(dev, DIF_MISC_CTRL,
+ vid_blk_read_word(dev, DIF_MISC_CTRL,
&dif_misc_ctrl_value);
dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF;
- status = vid_blk_write_word(dev, DIF_MISC_CTRL,
+ vid_blk_write_word(dev, DIF_MISC_CTRL,
dif_misc_ctrl_value);
}
@@ -1606,10 +1595,10 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq,
}
cx231xx_info("Enter IF=%zd\n",
- sizeof(Dif_set_array)/sizeof(struct dif_settings));
- for (i = 0; i < sizeof(Dif_set_array)/sizeof(struct dif_settings); i++) {
+ ARRAY_SIZE(Dif_set_array));
+ for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) {
if (Dif_set_array[i].if_freq == if_freq) {
- status = vid_blk_write_word(dev,
+ vid_blk_write_word(dev,
Dif_set_array[i].register_address, Dif_set_array[i].value);
}
}
@@ -3090,31 +3079,30 @@ int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len)
*/
int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len)
{
- int status = 0;
int i = 0;
/* get the lock */
mutex_lock(&dev->gpio_i2c_lock);
/* start */
- status = cx231xx_gpio_i2c_start(dev);
+ cx231xx_gpio_i2c_start(dev);
/* write dev_addr */
- status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1);
+ cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1);
/* read Ack */
- status = cx231xx_gpio_i2c_read_ack(dev);
+ cx231xx_gpio_i2c_read_ack(dev);
for (i = 0; i < len; i++) {
/* Write data */
- status = cx231xx_gpio_i2c_write_byte(dev, buf[i]);
+ cx231xx_gpio_i2c_write_byte(dev, buf[i]);
/* read Ack */
- status = cx231xx_gpio_i2c_read_ack(dev);
+ cx231xx_gpio_i2c_read_ack(dev);
}
/* write End */
- status = cx231xx_gpio_i2c_end(dev);
+ cx231xx_gpio_i2c_end(dev);
/* release the lock */
mutex_unlock(&dev->gpio_i2c_lock);
diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
index 08dd930f882..05358d48613 100644
--- a/drivers/media/video/cx231xx/cx231xx-core.c
+++ b/drivers/media/video/cx231xx/cx231xx-core.c
@@ -754,7 +754,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
}
}
- return 0;
+ return errCode ? -EINVAL : 0;
}
EXPORT_SYMBOL_GPL(cx231xx_set_mode);
@@ -764,7 +764,7 @@ int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size)
int actlen, ret = -ENOMEM;
u32 *buffer;
-buffer = kzalloc(4096, GFP_KERNEL);
+ buffer = kzalloc(4096, GFP_KERNEL);
if (buffer == NULL) {
cx231xx_info("out of mem\n");
return -ENOMEM;
@@ -772,16 +772,16 @@ buffer = kzalloc(4096, GFP_KERNEL);
memcpy(&buffer[0], firmware, 4096);
ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5),
- buffer, 4096, &actlen, 2000);
+ buffer, 4096, &actlen, 2000);
if (ret)
cx231xx_info("bulk message failed: %d (%d/%d)", ret,
- size, actlen);
+ size, actlen);
else {
errCode = actlen != size ? -1 : 0;
}
-kfree(buffer);
- return 0;
+ kfree(buffer);
+ return errCode;
}
/*****************************************************************
@@ -797,7 +797,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb)
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
- int rc, i;
+ int i;
switch (urb->status) {
case 0: /* success */
@@ -814,7 +814,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb)
/* Copy data from URB */
spin_lock(&dev->video_mode.slock);
- rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb);
+ dev->video_mode.isoc_ctl.isoc_copy(dev, urb);
spin_unlock(&dev->video_mode.slock);
/* Reset urb buffers */
@@ -822,7 +822,6 @@ static void cx231xx_isoc_irq_callback(struct urb *urb)
urb->iso_frame_desc[i].status = 0;
urb->iso_frame_desc[i].actual_length = 0;
}
- urb->status = 0;
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
@@ -843,7 +842,6 @@ static void cx231xx_bulk_irq_callback(struct urb *urb)
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
- int rc;
switch (urb->status) {
case 0: /* success */
@@ -860,12 +858,10 @@ static void cx231xx_bulk_irq_callback(struct urb *urb)
/* Copy data from URB */
spin_lock(&dev->video_mode.slock);
- rc = dev->video_mode.bulk_ctl.bulk_copy(dev, urb);
+ dev->video_mode.bulk_ctl.bulk_copy(dev, urb);
spin_unlock(&dev->video_mode.slock);
/* Reset urb buffers */
- urb->status = 0;
-
urb->status = usb_submit_urb(urb, GFP_ATOMIC);
if (urb->status) {
cx231xx_isocdbg("urb resubmit failed (error=%i)\n",
@@ -1231,42 +1227,40 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets,
EXPORT_SYMBOL_GPL(cx231xx_init_bulk);
void cx231xx_stop_TS1(struct cx231xx *dev)
{
- int status = 0;
u8 val[4] = { 0, 0, 0, 0 };
- val[0] = 0x00;
- val[1] = 0x03;
- val[2] = 0x00;
- val[3] = 0x00;
- status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
- TS_MODE_REG, val, 4);
-
- val[0] = 0x00;
- val[1] = 0x70;
- val[2] = 0x04;
- val[3] = 0x00;
- status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
- TS1_CFG_REG, val, 4);
+ val[0] = 0x00;
+ val[1] = 0x03;
+ val[2] = 0x00;
+ val[3] = 0x00;
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS_MODE_REG, val, 4);
+
+ val[0] = 0x00;
+ val[1] = 0x70;
+ val[2] = 0x04;
+ val[3] = 0x00;
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS1_CFG_REG, val, 4);
}
/* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */
void cx231xx_start_TS1(struct cx231xx *dev)
{
- int status = 0;
u8 val[4] = { 0, 0, 0, 0 };
- val[0] = 0x03;
- val[1] = 0x03;
- val[2] = 0x00;
- val[3] = 0x00;
- status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
- TS_MODE_REG, val, 4);
-
- val[0] = 0x04;
- val[1] = 0xA3;
- val[2] = 0x3B;
- val[3] = 0x00;
- status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
- TS1_CFG_REG, val, 4);
+ val[0] = 0x03;
+ val[1] = 0x03;
+ val[2] = 0x00;
+ val[3] = 0x00;
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS_MODE_REG, val, 4);
+
+ val[0] = 0x04;
+ val[1] = 0xA3;
+ val[2] = 0x3B;
+ val[3] = 0x00;
+ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
+ TS1_CFG_REG, val, 4);
}
/* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */
/*****************************************************************
diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c
index 8cdee5f78f1..3d15314e1f8 100644
--- a/drivers/media/video/cx231xx/cx231xx-vbi.c
+++ b/drivers/media/video/cx231xx/cx231xx-vbi.c
@@ -83,7 +83,6 @@ static inline void print_err_status(struct cx231xx *dev, int packet, int status)
*/
static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
{
- struct cx231xx_buffer *buf;
struct cx231xx_dmaqueue *dma_q = urb->context;
int rc = 1;
unsigned char *p_buffer;
@@ -102,8 +101,6 @@ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
return 0;
}
- buf = dev->vbi_mode.bulk_ctl.buf;
-
/* get buffer pointer and length */
p_buffer = urb->transfer_buffer;
buffer_size = urb->actual_length;
@@ -310,7 +307,6 @@ static void cx231xx_irq_vbi_callback(struct urb *urb)
struct cx231xx_video_mode *vmode =
container_of(dma_q, struct cx231xx_video_mode, vidq);
struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
- int rc;
switch (urb->status) {
case 0: /* success */
@@ -328,7 +324,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb)
/* Copy data from URB */
spin_lock(&dev->vbi_mode.slock);
- rc = dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
+ dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb);
spin_unlock(&dev->vbi_mode.slock);
/* Reset status */
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index 7f916f0685e..523aa49d6b8 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -326,9 +326,7 @@ static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
*/
static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
{
- struct cx231xx_buffer *buf;
struct cx231xx_dmaqueue *dma_q = urb->context;
- unsigned char *outp = NULL;
int i, rc = 1;
unsigned char *p_buffer;
u32 bytes_parsed = 0, buffer_size = 0;
@@ -346,10 +344,6 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
return 0;
}
- buf = dev->video_mode.isoc_ctl.buf;
- if (buf != NULL)
- outp = videobuf_to_vmalloc(&buf->vb);
-
for (i = 0; i < urb->number_of_packets; i++) {
int status = urb->iso_frame_desc[i].status;
@@ -429,9 +423,7 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
{
- struct cx231xx_buffer *buf;
struct cx231xx_dmaqueue *dma_q = urb->context;
- unsigned char *outp = NULL;
int rc = 1;
unsigned char *p_buffer;
u32 bytes_parsed = 0, buffer_size = 0;
@@ -449,10 +441,6 @@ static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb)
return 0;
}
- buf = dev->video_mode.bulk_ctl.buf;
- if (buf != NULL)
- outp = videobuf_to_vmalloc(&buf->vb);
-
if (1) {
/* get buffer pointer and length */
@@ -701,13 +689,9 @@ void cx231xx_reset_video_buffer(struct cx231xx *dev,
buf = dev->video_mode.bulk_ctl.buf;
if (buf == NULL) {
- u8 *outp = NULL;
/* first try to get the buffer */
get_next_buf(dma_q, &buf);
- if (buf)
- outp = videobuf_to_vmalloc(&buf->vb);
-
dma_q->pos = 0;
dma_q->field1_done = 0;
dma_q->current_field = -1;
@@ -2561,6 +2545,10 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
vfd->release = video_device_release;
vfd->debug = video_debug;
vfd->lock = &dev->lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 19b5499d262..13739e002a6 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -497,6 +497,10 @@ struct cx23885_board cx23885_boards[] = {
.name = "TerraTec Cinergy T PCIe Dual",
.portb = CX23885_MPEG_DVB,
.portc = CX23885_MPEG_DVB,
+ },
+ [CX23885_BOARD_TEVII_S471] = {
+ .name = "TeVii S471",
+ .portb = CX23885_MPEG_DVB,
}
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -705,6 +709,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x153b,
.subdevice = 0x117e,
.card = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL,
+ }, {
+ .subvendor = 0xd471,
+ .subdevice = 0x9022,
+ .card = CX23885_BOARD_TEVII_S471,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -1460,6 +1468,7 @@ void cx23885_card_setup(struct cx23885_dev *dev)
ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
break;
case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_TEVII_S471:
case CX23885_BOARD_DVBWORLD_2005:
ts1->gen_ctrl_val = 0x5; /* Parallel */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
index 6ad227029a0..697728f0943 100644
--- a/drivers/media/video/cx23885/cx23885-core.c
+++ b/drivers/media/video/cx23885/cx23885-core.c
@@ -1046,6 +1046,13 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
if (cx23885_boards[dev->board].ci_type > 0)
cx_clear(RDR_RDRCTL1, 1 << 8);
+ switch (dev->board) {
+ case CX23885_BOARD_TEVII_S470:
+ case CX23885_BOARD_TEVII_S471:
+ cx_clear(RDR_RDRCTL1, 1 << 8);
+ break;
+ }
+
return 0;
}
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 6835eb1fc09..a80a92c4745 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -1173,6 +1173,13 @@ static int dvb_register(struct cx23885_tsport *port)
break;
}
break;
+ case CX23885_BOARD_TEVII_S471:
+ i2c_bus = &dev->i2c_bus[1];
+
+ fe0->dvb.frontend = dvb_attach(ds3000_attach,
+ &tevii_ds3000_config,
+ &i2c_bus->i2c_adap);
+ break;
default:
printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
" isn't supported yet\n",
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index f020f0568df..d884784a1c8 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -89,6 +89,7 @@
#define CX23885_BOARD_MPX885 32
#define CX23885_BOARD_MYGICA_X8507 33
#define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34
+#define CX23885_BOARD_TEVII_S471 35
#define GPIO_0 0x00000001
#define GPIO_1 0x00000002
diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c
index bb1ce346425..c2bc39c58f8 100644
--- a/drivers/media/video/cx23885/cx23888-ir.c
+++ b/drivers/media/video/cx23885/cx23888-ir.c
@@ -331,9 +331,7 @@ static u64 ns_to_pulse_clocks(u32 ns)
static u16 pulse_clocks_to_clock_divider(u64 count)
{
- u32 rem;
-
- rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+ do_div(count, (FIFO_RXTX << 2) | 0x3);
/* net result needs to be rounded down and decremented by 1 */
if (count > RXCLK_RCD + 1)
diff --git a/drivers/media/video/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c
index 03cfac476b0..1858a45dd08 100644
--- a/drivers/media/video/cx25821/cx25821-alsa.c
+++ b/drivers/media/video/cx25821/cx25821-alsa.c
@@ -290,11 +290,9 @@ static irqreturn_t cx25821_irq(int irq, void *dev_id)
u32 status, pci_status;
u32 audint_status, audint_mask;
int loop, handled = 0;
- int audint_count = 0;
audint_status = cx_read(AUD_A_INT_STAT);
audint_mask = cx_read(AUD_A_INT_MSK);
- audint_count = cx_read(AUD_A_GPCNT);
status = cx_read(PCI_INT_STAT);
for (loop = 0; loop < 1; loop++) {
diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c
index 20c7ca3351a..8b2a99975c2 100644
--- a/drivers/media/video/cx25821/cx25821-audio-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c
@@ -585,7 +585,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num,
static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id)
{
struct cx25821_dev *dev = dev_id;
- u32 msk_stat, audio_status;
+ u32 audio_status;
int handled = 0;
struct sram_channel *sram_ch;
@@ -594,7 +594,6 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id)
sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels;
- msk_stat = cx_read(sram_ch->int_mstat);
audio_status = cx_read(sram_ch->int_stat);
/* Only deal with our interrupt */
diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c
index 7930ca58349..83c1aa6b2e6 100644
--- a/drivers/media/video/cx25821/cx25821-core.c
+++ b/drivers/media/video/cx25821/cx25821-core.c
@@ -379,14 +379,6 @@ static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
return cx_read(bus->reg_stat) & 0x01;
}
-void cx_i2c_read_print(struct cx25821_dev *dev, u32 reg, const char *reg_string)
-{
- int tmp = 0;
- u32 value = 0;
-
- value = cx25821_i2c_read(&dev->i2c_bus[0], reg, &tmp);
-}
-
static void cx25821_registers_init(struct cx25821_dev *dev)
{
u32 tmp;
@@ -895,7 +887,7 @@ static void cx25821_iounmap(struct cx25821_dev *dev)
static int cx25821_dev_setup(struct cx25821_dev *dev)
{
- int io_size = 0, i;
+ int i;
pr_info("\n***********************************\n");
pr_info("cx25821 set up\n");
@@ -960,7 +952,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev)
/* PCIe stuff */
dev->base_io_addr = pci_resource_start(dev->pci, 0);
- io_size = pci_resource_len(dev->pci, 0);
if (!dev->base_io_addr) {
CX25821_ERR("No PCI Memory resources, exiting!\n");
@@ -1317,13 +1308,12 @@ void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf)
static irqreturn_t cx25821_irq(int irq, void *dev_id)
{
struct cx25821_dev *dev = dev_id;
- u32 pci_status, pci_mask;
+ u32 pci_status;
u32 vid_status;
int i, handled = 0;
u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 };
pci_status = cx_read(PCI_INT_STAT);
- pci_mask = cx_read(PCI_INT_MSK);
if (pci_status == 0)
goto out;
diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c
index 12d7300fa1e..6311180f430 100644
--- a/drivers/media/video/cx25821/cx25821-i2c.c
+++ b/drivers/media/video/cx25821/cx25821-i2c.c
@@ -361,7 +361,6 @@ void cx25821_av_clk(struct cx25821_dev *dev, int enable)
int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value)
{
struct i2c_client *client = &bus->i2c_client;
- int retval = 0;
int v = 0;
u8 addr[2] = { 0, 0 };
u8 buf[4] = { 0, 0, 0, 0 };
@@ -385,7 +384,7 @@ int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value)
msgs[0].addr = 0x44;
msgs[1].addr = 0x44;
- retval = i2c_xfer(client->adapter, msgs, 2);
+ i2c_xfer(client->adapter, msgs, 2);
v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
*value = v;
diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c
index 298a68d98c2..313fb20a0b4 100644
--- a/drivers/media/video/cx25821/cx25821-medusa-video.c
+++ b/drivers/media/video/cx25821/cx25821-medusa-video.c
@@ -35,7 +35,6 @@
static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel,
int enable)
{
- int ret_val = 1;
u32 value = 0;
u32 tmp = 0;
int out_ctrl = OUT_CTRL1;
@@ -79,13 +78,13 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel,
value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */
if (enable)
value |= 0x00000080; /* set BLUE_FIELD_EN */
- ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value);
+ cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value);
value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp);
value &= 0xFFFFFF7F;
if (enable)
value |= 0x00000080; /* set BLUE_FIELD_EN */
- ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value);
+ cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value);
}
static int medusa_initialize_ntsc(struct cx25821_dev *dev)
@@ -431,7 +430,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width,
{
int decoder = 0;
int decoder_count = 0;
- int ret_val = 0;
u32 hscale = 0x0;
u32 vscale = 0x0;
const int MAX_WIDTH = 720;
@@ -482,9 +480,9 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width,
for (; decoder < decoder_count; decoder++) {
/* write scaling values for each decoder */
- ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ cx25821_i2c_write(&dev->i2c_bus[0],
HSCALE_CTRL + (0x200 * decoder), hscale);
- ret_val = cx25821_i2c_write(&dev->i2c_bus[0],
+ cx25821_i2c_write(&dev->i2c_bus[0],
VSCALE_CTRL + (0x200 * decoder), vscale);
}
@@ -494,7 +492,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width,
static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
int duration)
{
- int ret_val = 0;
u32 fld_cnt = 0;
u32 tmp = 0;
u32 disp_cnt_reg = DISP_AB_CNT;
@@ -537,7 +534,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder,
fld_cnt |= ((u32) duration) << 16;
}
- ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt);
+ cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt);
mutex_unlock(&dev->lock);
}
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
index 5a157cf4a95..c8c94fbf5d8 100644
--- a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c
@@ -587,7 +587,7 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num,
static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id)
{
struct cx25821_dev *dev = dev_id;
- u32 msk_stat, vid_status;
+ u32 vid_status;
int handled = 0;
int channel_num = 0;
struct sram_channel *sram_ch;
@@ -598,7 +598,6 @@ static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id)
channel_num = VID_UPSTREAM_SRAM_CHANNEL_J;
sram_ch = dev->channels[channel_num].sram_channels;
- msk_stat = cx_read(sram_ch->int_mstat);
vid_status = cx_read(sram_ch->int_stat);
/* Only deal with our interrupt */
diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c
index 21e7d657f04..52c13e0b649 100644
--- a/drivers/media/video/cx25821/cx25821-video-upstream.c
+++ b/drivers/media/video/cx25821/cx25821-video-upstream.c
@@ -637,7 +637,7 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num,
static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id)
{
struct cx25821_dev *dev = dev_id;
- u32 msk_stat, vid_status;
+ u32 vid_status;
int handled = 0;
int channel_num = 0;
struct sram_channel *sram_ch;
@@ -649,7 +649,6 @@ static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id)
sram_ch = dev->channels[channel_num].sram_channels;
- msk_stat = cx_read(sram_ch->int_mstat);
vid_status = cx_read(sram_ch->int_stat);
/* Only deal with our interrupt */
diff --git a/drivers/media/video/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c
index ffd8bc79c02..b38d4379cc3 100644
--- a/drivers/media/video/cx25821/cx25821-video.c
+++ b/drivers/media/video/cx25821/cx25821-video.c
@@ -109,25 +109,6 @@ struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
return NULL;
}
-void cx25821_dump_video_queue(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q)
-{
- struct cx25821_buffer *buf;
- struct list_head *item;
- dprintk(1, "%s()\n", __func__);
-
- if (!list_empty(&q->active)) {
- list_for_each(item, &q->active)
- buf = list_entry(item, struct cx25821_buffer, vb.queue);
- }
-
- if (!list_empty(&q->queued)) {
- list_for_each(item, &q->queued)
- buf = list_entry(item, struct cx25821_buffer, vb.queue);
- }
-
-}
-
void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q,
u32 count)
{
@@ -557,7 +538,7 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
struct cx25821_buffer *buf =
container_of(vb, struct cx25821_buffer, vb);
int rc, init_buffer = 0;
- u32 line0_offset, line1_offset;
+ u32 line0_offset;
struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
int bpl_local = LINE_SIZE_D1;
int channel_opened = fh->channel_id;
@@ -639,7 +620,6 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
case V4L2_FIELD_INTERLACED:
/* All other formats are top field first */
line0_offset = 0;
- line1_offset = buf->bpl;
dprintk(1, "top field first\n");
cx25821_risc_buffer(dev->pci, &buf->risc,
@@ -1830,7 +1810,6 @@ static long video_ioctl_set(struct file *file, unsigned int cmd,
int i = 0;
int cif_enable = 0;
int cif_width = 0;
- u32 value = 0;
data_from_user = (struct downstream_user_struct *)arg;
@@ -1914,7 +1893,7 @@ static long video_ioctl_set(struct file *file, unsigned int cmd,
cx_write(data_from_user->reg_address, data_from_user->reg_data);
break;
case MEDUSA_READ:
- value = cx25821_i2c_read(&dev->i2c_bus[0],
+ cx25821_i2c_read(&dev->i2c_bus[0],
(u16) data_from_user->reg_address,
&data_from_user->reg_data);
break;
diff --git a/drivers/media/video/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h
index d0d9538ca5b..9652a5e35ba 100644
--- a/drivers/media/video/cx25821/cx25821-video.h
+++ b/drivers/media/video/cx25821/cx25821-video.h
@@ -86,8 +86,6 @@ extern struct cx25821_fmt formats[];
extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc);
extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM];
-extern void cx25821_dump_video_queue(struct cx25821_dev *dev,
- struct cx25821_dmaqueue *q);
extern void cx25821_video_wakeup(struct cx25821_dev *dev,
struct cx25821_dmaqueue *q, u32 count);
diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c
index 13c380ebb56..38ce76ed192 100644
--- a/drivers/media/video/cx25840/cx25840-ir.c
+++ b/drivers/media/video/cx25840/cx25840-ir.c
@@ -316,9 +316,7 @@ static u64 ns_to_pulse_clocks(u32 ns)
static u16 pulse_clocks_to_clock_divider(u64 count)
{
- u32 rem;
-
- rem = do_div(count, (FIFO_RXTX << 2) | 0x3);
+ do_div(count, (FIFO_RXTX << 2) | 0x3);
/* net result needs to be rounded down and decremented by 1 */
if (count > RXCLK_RCD + 1)
@@ -860,12 +858,10 @@ static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
ssize_t *num)
{
struct cx25840_ir_state *ir_state = to_ir_state(sd);
- struct i2c_client *c;
if (ir_state == NULL)
return -ENODEV;
- c = ir_state->c;
#if 0
/*
* FIXME - the code below is an incomplete and untested sketch of what
diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig
index 60a456ebdc7..9337b5605c9 100644
--- a/drivers/media/video/davinci/Kconfig
+++ b/drivers/media/video/davinci/Kconfig
@@ -40,6 +40,7 @@ config VIDEO_VPSS_SYSTEM
config VIDEO_VPFE_CAPTURE
tristate "VPFE Video Capture Driver"
depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3)
+ depends on I2C
select VIDEOBUF_DMA_CONTIG
help
Support for DMx/AMx VPFE based frame grabber. This is the
diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c
index 1f3b1c72925..e106b72810a 100644
--- a/drivers/media/video/davinci/vpbe_display.c
+++ b/drivers/media/video/davinci/vpbe_display.c
@@ -1618,6 +1618,10 @@ static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev,
vbd->ioctl_ops = &vpbe_ioctl_ops;
vbd->minor = -1;
vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vbd->flags);
vbd->lock = &vpbe_display_layer->opslock;
if (disp_dev->vpbe_dev->current_timings.timings_type &
diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c
index 20cf271a774..49a845fb804 100644
--- a/drivers/media/video/davinci/vpfe_capture.c
+++ b/drivers/media/video/davinci/vpfe_capture.c
@@ -1761,7 +1761,7 @@ static long vpfe_param_handler(struct file *file, void *priv,
}
break;
default:
- ret = -EINVAL;
+ ret = -ENOTTY;
}
unlock_out:
mutex_unlock(&vpfe_dev->lock);
diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c
index 6504e40a31d..96046957bf2 100644
--- a/drivers/media/video/davinci/vpif_capture.c
+++ b/drivers/media/video/davinci/vpif_capture.c
@@ -2228,6 +2228,10 @@ static __init int vpif_probe(struct platform_device *pdev)
common = &(ch->common[VPIF_VIDEO_INDEX]);
spin_lock_init(&common->irqlock);
mutex_init(&common->lock);
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags);
ch->video_dev->lock = &common->lock;
/* Initialize prio member of channel object */
v4l2_prio_init(&ch->prio);
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
index 7fa34b4fae2..e6488ee7db1 100644
--- a/drivers/media/video/davinci/vpif_display.c
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -1778,6 +1778,10 @@ static __init int vpif_probe(struct platform_device *pdev)
v4l2_prio_init(&ch->prio);
ch->common[VPIF_VIDEO_INDEX].fmt.type =
V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags);
ch->video_dev->lock = &common->lock;
/* register video device */
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index f6f622e123b..928ef0d0429 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -49,10 +49,10 @@ config VIDEO_EM28XX_DVB
Empiatech em28xx chips.
config VIDEO_EM28XX_RC
- bool "EM28XX Remote Controller support"
+ tristate "EM28XX Remote Controller support"
depends on RC_CORE
depends on VIDEO_EM28XX
depends on !(RC_CORE=m && VIDEO_EM28XX=y)
- default y
+ default VIDEO_EM28XX
---help---
Enables Remote Controller support on em28xx driver.
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 2abdf76c520..c8b338d4be0 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -1,16 +1,15 @@
em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o
em28xx-y += em28xx-core.o em28xx-vbi.o
-em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o
-
em28xx-alsa-objs := em28xx-audio.o
+em28xx-rc-objs := em28xx-input.o
obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o
obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o
obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o
+obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o
ccflags-y += -Idrivers/media/video
ccflags-y += -Idrivers/media/common/tuners
ccflags-y += -Idrivers/media/dvb/dvb-core
ccflags-y += -Idrivers/media/dvb/frontends
-
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index e2a7b77c39c..d7e2a3dc552 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -343,7 +343,6 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream)
static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- unsigned int channels, rate, format;
int ret;
dprintk("Setting capture parameters\n");
@@ -352,13 +351,17 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream,
params_buffer_bytes(hw_params));
if (ret < 0)
return ret;
+#if 0
+ /* TODO: set up em28xx audio chip to deliver the correct audio format,
+ current default is 48000hz multiplexed => 96000hz mono
+ which shouldn't matter since analogue TV only supports mono */
+ unsigned int channels, rate, format;
+
format = params_format(hw_params);
rate = params_rate(hw_params);
channels = params_channels(hw_params);
+#endif
- /* TODO: set up em28xx audio chip to deliver the correct audio format,
- current default is 48000hz multiplexed => 96000hz mono
- which shouldn't matter since analogue TV only supports mono */
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 9fd8cc7dbb2..20a7e24de6f 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -69,6 +69,8 @@ struct em28xx_hash_table {
unsigned int tuner;
};
+static void em28xx_pre_card_setup(struct em28xx *dev);
+
/*
* Reset sequences for analog/digital modes
*/
@@ -2361,7 +2363,7 @@ static int em28xx_hint_sensor(struct em28xx *dev)
/* Since em28xx_pre_card_setup() requires a proper dev->model,
* this won't work for boards with generic PCI IDs
*/
-void em28xx_pre_card_setup(struct em28xx *dev)
+static void em28xx_pre_card_setup(struct em28xx *dev)
{
/* Set the initial XCLK and I2C clock values based on the board
definition */
@@ -2661,55 +2663,7 @@ static int em28xx_hint_board(struct em28xx *dev)
return -1;
}
-/* ----------------------------------------------------------------------- */
-void em28xx_register_i2c_ir(struct em28xx *dev)
-{
- /* Leadtek winfast tv USBII deluxe can find a non working IR-device */
- /* at address 0x18, so if that address is needed for another board in */
- /* the future, please put it after 0x1f. */
- struct i2c_board_info info;
- const unsigned short addr_list[] = {
- 0x1f, 0x30, 0x47, I2C_CLIENT_END
- };
-
- if (disable_ir)
- return;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- memset(&dev->init_data, 0, sizeof(dev->init_data));
- strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
-
- /* detect & configure */
- switch (dev->model) {
- case EM2800_BOARD_TERRATEC_CINERGY_200:
- case EM2820_BOARD_TERRATEC_CINERGY_250:
- dev->init_data.ir_codes = RC_MAP_EM_TERRATEC;
- dev->init_data.get_key = em28xx_get_key_terratec;
- dev->init_data.name = "i2c IR (EM28XX Terratec)";
- break;
- case EM2820_BOARD_PINNACLE_USB_2:
- dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
- dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey;
- dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)";
- break;
- case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
- dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
- dev->init_data.get_key = em28xx_get_key_em_haup;
- dev->init_data.name = "i2c IR (EM2840 Hauppauge)";
- break;
- case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
- dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE;
- dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe;
- dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)";
- break;
- }
-
- if (dev->init_data.name)
- info.platform_data = &dev->init_data;
- i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL);
-}
-
-void em28xx_card_setup(struct em28xx *dev)
+static void em28xx_card_setup(struct em28xx *dev)
{
/*
* If the device can be a webcam, seek for a sensor.
@@ -2849,13 +2803,6 @@ void em28xx_card_setup(struct em28xx *dev)
break;
}
-#if defined(CONFIG_MODULES) && defined(MODULE)
- if (dev->board.has_ir_i2c && !disable_ir)
- request_module("ir-kbd-i2c");
-#endif
- if (dev->board.has_snapshot_button)
- em28xx_register_snapshot_button(dev);
-
if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) {
em28xx_errdev("\n\n");
em28xx_errdev("The support for this board weren't "
@@ -2929,9 +2876,6 @@ void em28xx_card_setup(struct em28xx *dev)
}
em28xx_tuner_setup(dev);
-
- if(!disable_ir)
- em28xx_ir_init(dev);
}
@@ -2948,6 +2892,8 @@ static void request_module_async(struct work_struct *work)
if (dev->board.has_dvb)
request_module("em28xx-dvb");
+ if (dev->board.has_ir_i2c && !disable_ir)
+ request_module("em28xx-rc");
}
static void request_modules(struct em28xx *dev)
@@ -2972,12 +2918,6 @@ static void flush_request_modules(struct em28xx *dev)
*/
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);
@@ -3005,9 +2945,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
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;
@@ -3140,9 +3077,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev,
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
- INIT_LIST_HEAD(&dev->vidq.queued);
INIT_LIST_HEAD(&dev->vbiq.active);
- INIT_LIST_HEAD(&dev->vbiq.queued);
if (dev->board.has_msp34xx) {
/* Send a reset to other chips via gpio */
@@ -3447,8 +3382,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
resources */
mutex_lock(&dev->lock);
- wake_up_interruptible_all(&dev->open);
-
v4l2_device_disconnect(&dev->v4l2_dev);
if (dev->users) {
@@ -3460,8 +3393,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
dev->state |= DEV_MISCONFIGURED;
em28xx_uninit_isoc(dev, dev->mode);
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);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 53a9fb91e97..5717bdee8f1 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -139,6 +139,7 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg)
{
return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg);
}
+EXPORT_SYMBOL_GPL(em28xx_read_reg);
/*
* em28xx_write_regs_req()
@@ -205,6 +206,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
return rc;
}
+EXPORT_SYMBOL_GPL(em28xx_write_regs);
/* Write a single register */
int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
@@ -239,6 +241,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
return em28xx_write_regs(dev, reg, &newval, 1);
}
+EXPORT_SYMBOL_GPL(em28xx_write_reg_bits);
/*
* em28xx_is_ac97_ready()
@@ -666,7 +669,6 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
-EXPORT_SYMBOL_GPL(em28xx_capture_start);
int em28xx_vbi_supported(struct em28xx *dev)
{
@@ -975,7 +977,6 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode)
else
isoc_bufs = &dev->isoc_ctl.analog_bufs;
- dev->isoc_ctl.nfields = -1;
for (i = 0; i < isoc_bufs->num_bufs; i++) {
urb = isoc_bufs->urb[i];
if (urb) {
@@ -1008,6 +1009,31 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode)
EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
/*
+ * Stop URBs
+ */
+void em28xx_stop_urbs(struct em28xx *dev)
+{
+ int i;
+ struct urb *urb;
+ struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs;
+
+ em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n");
+
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = isoc_bufs->urb[i];
+ if (urb) {
+ if (!irqs_disabled())
+ usb_kill_urb(urb);
+ else
+ usb_unlink_urb(urb);
+ }
+ }
+
+ em28xx_capture_start(dev, 0);
+}
+EXPORT_SYMBOL_GPL(em28xx_stop_urbs);
+
+/*
* Allocate URBs
*/
int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index 503a8d5b538..16410ac2009 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -183,7 +183,7 @@ static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
{
struct em28xx *dev = dvb->adapter.priv;
- em28xx_capture_start(dev, 0);
+ em28xx_stop_urbs(dev);
em28xx_set_mode(dev, EM28XX_SUSPEND);
@@ -336,6 +336,8 @@ struct drxk_config pctv_520e_drxk = {
.single_master = 1,
.microcode_name = "dvb-demod-drxk-pctv.fw",
.chunk_size = 58,
+ .antenna_dvbt = true, /* disable LNA */
+ .antenna_gpio = (1 << 2), /* disable LNA */
};
static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
@@ -474,8 +476,8 @@ static void terratec_h5_init(struct em28xx *dev)
static void pctv_520e_init(struct em28xx *dev)
{
/*
- * Init TDA8295(?) analog demodulator. Looks like I2C traffic to
- * digital demodulator and tuner are routed via TDA8295.
+ * Init AVF4910B analog decoder. Looks like I2C traffic to
+ * digital demodulator and tuner are routed via AVF4910B.
*/
int i;
struct {
@@ -542,7 +544,8 @@ static struct cxd2820r_config em28xx_cxd2820r_config = {
.i2c_address = (0xd8 >> 1),
.ts_mode = CXD2820R_TS_SERIAL,
- /* enable LNA for DVB-T2 and DVB-C */
+ /* enable LNA for DVB-T, DVB-T2 and DVB-C */
+ .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
.gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
.gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L,
};
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index a88e169dba2..185db65b766 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -553,9 +553,6 @@ int em28xx_i2c_register(struct em28xx *dev)
if (i2c_scan)
em28xx_do_i2c_scan(dev);
- /* Instantiate the IR receiver device, if present */
- em28xx_register_i2c_ir(dev);
-
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
index 2630b265b0e..fce5f7680c9 100644
--- a/drivers/media/video/em28xx/em28xx-input.c
+++ b/drivers/media/video/em28xx/em28xx-input.c
@@ -80,7 +80,7 @@ struct em28xx_IR {
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)
+static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char b;
@@ -108,7 +108,7 @@ 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)
+static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char buf[2];
u16 code;
@@ -157,7 +157,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
return 1;
}
-int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
+static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
u32 *ir_raw)
{
unsigned char buf[3];
@@ -179,7 +179,8 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key,
return 1;
}
-int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
{
unsigned char subaddr, keydetect, key;
@@ -387,7 +388,138 @@ int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type)
return rc;
}
-int em28xx_ir_init(struct em28xx *dev)
+static void em28xx_register_i2c_ir(struct em28xx *dev)
+{
+ /* Leadtek winfast tv USBII deluxe can find a non working IR-device */
+ /* at address 0x18, so if that address is needed for another board in */
+ /* the future, please put it after 0x1f. */
+ struct i2c_board_info info;
+ const unsigned short addr_list[] = {
+ 0x1f, 0x30, 0x47, I2C_CLIENT_END
+ };
+
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ memset(&dev->init_data, 0, sizeof(dev->init_data));
+ strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
+
+ /* detect & configure */
+ switch (dev->model) {
+ case EM2800_BOARD_TERRATEC_CINERGY_200:
+ case EM2820_BOARD_TERRATEC_CINERGY_250:
+ dev->init_data.ir_codes = RC_MAP_EM_TERRATEC;
+ dev->init_data.get_key = em28xx_get_key_terratec;
+ dev->init_data.name = "i2c IR (EM28XX Terratec)";
+ break;
+ case EM2820_BOARD_PINNACLE_USB_2:
+ dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY;
+ dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey;
+ dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)";
+ break;
+ case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
+ dev->init_data.ir_codes = RC_MAP_HAUPPAUGE;
+ dev->init_data.get_key = em28xx_get_key_em_haup;
+ dev->init_data.name = "i2c IR (EM2840 Hauppauge)";
+ break;
+ case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE:
+ dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE;
+ dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe;
+ dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)";
+ break;
+ }
+
+ if (dev->init_data.name)
+ info.platform_data = &dev->init_data;
+ i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL);
+}
+
+/**********************************************************
+ Handle Webcam snapshot button
+ **********************************************************/
+
+static void em28xx_query_sbutton(struct work_struct *work)
+{
+ /* Poll the register and see if the button is depressed */
+ struct em28xx *dev =
+ container_of(work, struct em28xx, sbutton_query_work.work);
+ int ret;
+
+ ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP);
+
+ if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) {
+ u8 cleared;
+ /* Button is depressed, clear the register */
+ cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT;
+ em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1);
+
+ /* Not emulate the keypress */
+ input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+ 1);
+ /* Now unpress the key */
+ input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
+ 0);
+ }
+
+ /* Schedule next poll */
+ schedule_delayed_work(&dev->sbutton_query_work,
+ msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+}
+
+static void em28xx_register_snapshot_button(struct em28xx *dev)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ em28xx_info("Registering snapshot button...\n");
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ em28xx_errdev("input_allocate_device failed\n");
+ return;
+ }
+
+ usb_make_path(dev->udev, dev->snapshot_button_path,
+ sizeof(dev->snapshot_button_path));
+ strlcat(dev->snapshot_button_path, "/sbutton",
+ sizeof(dev->snapshot_button_path));
+ INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton);
+
+ input_dev->name = "em28xx snapshot button";
+ input_dev->phys = dev->snapshot_button_path;
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit);
+ input_dev->keycodesize = 0;
+ input_dev->keycodemax = 0;
+ input_dev->id.bustype = BUS_USB;
+ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
+ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
+ input_dev->id.version = 1;
+ input_dev->dev.parent = &dev->udev->dev;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ em28xx_errdev("input_register_device failed\n");
+ input_free_device(input_dev);
+ return;
+ }
+
+ dev->sbutton_input_dev = input_dev;
+ schedule_delayed_work(&dev->sbutton_query_work,
+ msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+ return;
+
+}
+
+static void em28xx_deregister_snapshot_button(struct em28xx *dev)
+{
+ if (dev->sbutton_input_dev != NULL) {
+ em28xx_info("Deregistering snapshot button\n");
+ cancel_delayed_work_sync(&dev->sbutton_query_work);
+ input_unregister_device(dev->sbutton_input_dev);
+ dev->sbutton_input_dev = NULL;
+ }
+ return;
+}
+
+static int em28xx_ir_init(struct em28xx *dev)
{
struct em28xx_IR *ir;
struct rc_dev *rc;
@@ -448,6 +580,15 @@ int em28xx_ir_init(struct em28xx *dev)
if (err)
goto err_out_stop;
+ em28xx_register_i2c_ir(dev);
+
+#if defined(CONFIG_MODULES) && defined(MODULE)
+ if (dev->board.has_ir_i2c)
+ request_module("ir-kbd-i2c");
+#endif
+ if (dev->board.has_snapshot_button)
+ em28xx_register_snapshot_button(dev);
+
return 0;
err_out_stop:
@@ -458,10 +599,12 @@ int em28xx_ir_init(struct em28xx *dev)
return err;
}
-int em28xx_ir_fini(struct em28xx *dev)
+static int em28xx_ir_fini(struct em28xx *dev)
{
struct em28xx_IR *ir = dev->ir;
+ em28xx_deregister_snapshot_button(dev);
+
/* skip detach on non attached boards */
if (!ir)
return 0;
@@ -475,89 +618,26 @@ int em28xx_ir_fini(struct em28xx *dev)
return 0;
}
-/**********************************************************
- Handle Webcam snapshot button
- **********************************************************/
+static struct em28xx_ops rc_ops = {
+ .id = EM28XX_RC,
+ .name = "Em28xx Input Extension",
+ .init = em28xx_ir_init,
+ .fini = em28xx_ir_fini,
+};
-static void em28xx_query_sbutton(struct work_struct *work)
+static int __init em28xx_rc_register(void)
{
- /* Poll the register and see if the button is depressed */
- struct em28xx *dev =
- container_of(work, struct em28xx, sbutton_query_work.work);
- int ret;
-
- ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP);
-
- if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) {
- u8 cleared;
- /* Button is depressed, clear the register */
- cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT;
- em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1);
-
- /* Not emulate the keypress */
- input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
- 1);
- /* Now unpress the key */
- input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY,
- 0);
- }
-
- /* Schedule next poll */
- schedule_delayed_work(&dev->sbutton_query_work,
- msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
+ return em28xx_register_extension(&rc_ops);
}
-void em28xx_register_snapshot_button(struct em28xx *dev)
+static void __exit em28xx_rc_unregister(void)
{
- struct input_dev *input_dev;
- int err;
-
- em28xx_info("Registering snapshot button...\n");
- input_dev = input_allocate_device();
- if (!input_dev) {
- em28xx_errdev("input_allocate_device failed\n");
- return;
- }
-
- usb_make_path(dev->udev, dev->snapshot_button_path,
- sizeof(dev->snapshot_button_path));
- strlcat(dev->snapshot_button_path, "/sbutton",
- sizeof(dev->snapshot_button_path));
- INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton);
-
- input_dev->name = "em28xx snapshot button";
- input_dev->phys = dev->snapshot_button_path;
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
- set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit);
- input_dev->keycodesize = 0;
- input_dev->keycodemax = 0;
- input_dev->id.bustype = BUS_USB;
- input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
- input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
- input_dev->id.version = 1;
- input_dev->dev.parent = &dev->udev->dev;
-
- err = input_register_device(input_dev);
- if (err) {
- em28xx_errdev("input_register_device failed\n");
- input_free_device(input_dev);
- return;
- }
-
- dev->sbutton_input_dev = input_dev;
- schedule_delayed_work(&dev->sbutton_query_work,
- msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL));
- return;
-
+ em28xx_unregister_extension(&rc_ops);
}
-void em28xx_deregister_snapshot_button(struct em28xx *dev)
-{
- if (dev->sbutton_input_dev != NULL) {
- em28xx_info("Deregistering snapshot button\n");
- cancel_delayed_work_sync(&dev->sbutton_query_work);
- input_unregister_device(dev->sbutton_input_dev);
- dev->sbutton_input_dev = NULL;
- }
- return;
-}
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_DESCRIPTION("Em28xx Input driver");
+
+module_init(em28xx_rc_register);
+module_exit(em28xx_rc_unregister);
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 324b695c072..50f5f4fc214 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -1305,9 +1305,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
if (0 == INPUT(i)->type)
return -EINVAL;
- dev->ctl_input = i;
-
- video_mux(dev, dev->ctl_input);
+ video_mux(dev, i);
return 0;
}
@@ -2262,6 +2260,7 @@ static int em28xx_v4l2_close(struct file *filp)
em28xx_release_resources(dev);
kfree(dev->alt_max_pkt_size);
kfree(dev);
+ kfree(fh);
return 0;
}
@@ -2286,7 +2285,6 @@ static int em28xx_v4l2_close(struct file *filp)
videobuf_mmap_free(&fh->vb_vbiq);
kfree(fh);
dev->users--;
- wake_up_interruptible_nr(&dev->open, 1);
return 0;
}
@@ -2497,6 +2495,10 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
vfd->release = video_device_release;
vfd->debug = video_debug;
vfd->lock = &dev->lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
snprintf(vfd->name, sizeof(vfd->name), "%s %s",
dev->name, type_name);
@@ -2518,7 +2520,6 @@ int em28xx_register_analog_devices(struct em28xx *dev)
dev->norm = em28xx_video_template.current_norm;
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm);
dev->interlaced = EM28XX_INTERLACED_DEFAULT;
- dev->ctl_input = 0;
/* Analog specific initialization */
dev->format = &format[0];
@@ -2532,7 +2533,7 @@ int em28xx_register_analog_devices(struct em28xx *dev)
em28xx_set_video_format(dev, format[0].fourcc,
maxw, norm_maxh(dev));
- video_mux(dev, dev->ctl_input);
+ video_mux(dev, 0);
/* Audio defaults */
dev->mute = 1;
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 2868b19f8b5..8757523e686 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -226,24 +226,10 @@ struct em28xx_usb_isoc_ctl {
/* isoc transfer buffers for digital mode */
struct em28xx_usb_isoc_bufs digital_bufs;
- /* Last buffer command and region */
- u8 cmd;
- int pos, size, pktsize;
-
- /* Last field: ODD or EVEN? */
- int field;
-
- /* Stores incomplete commands */
- u32 tmp_buf;
- int tmp_buf_len;
-
/* Stores already requested buffers */
struct em28xx_buffer *vid_buf;
struct em28xx_buffer *vbi_buf;
- /* Stores the number of received fields */
- int nfields;
-
/* isoc urb callback */
int (*isoc_copy) (struct em28xx *dev, struct urb *urb);
@@ -264,12 +250,10 @@ struct em28xx_buffer {
struct list_head frame;
int top_field;
- int receiving;
};
struct em28xx_dmaqueue {
struct list_head active;
- struct list_head queued;
wait_queue_head_t wq;
@@ -277,13 +261,6 @@ struct em28xx_dmaqueue {
int pos;
};
-/* io methods */
-enum em28xx_io_method {
- IO_NONE,
- IO_READ,
- IO_MMAP,
-};
-
/* inputs */
#define MAX_EM28XX_INPUT 4
@@ -467,6 +444,7 @@ enum em28xx_dev_state {
/* em28xx extensions */
#define EM28XX_AUDIO 0x10
#define EM28XX_DVB 0x20
+#define EM28XX_RC 0x30
/* em28xx resource types (used for res_get/res_lock etc */
#define EM28XX_RESOURCE_VIDEO 0x01
@@ -577,7 +555,6 @@ struct em28xx {
/* states */
enum em28xx_dev_state state;
- enum em28xx_io_method io;
/* vbi related state tracking */
int capture_type;
@@ -593,7 +570,6 @@ struct em28xx {
struct mutex ctrl_urb_lock; /* protects urb_buf */
/* spinlock_t queue_lock; */
struct list_head inqueue, outqueue;
- wait_queue_head_t open, wait_frame, wait_stream;
struct video_device *vbi_dev;
struct video_device *radio_dev;
@@ -695,6 +671,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
int max_packets, int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct em28xx *dev, struct urb *urb));
void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode);
+void em28xx_stop_urbs(struct em28xx *dev);
int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
@@ -710,45 +687,12 @@ void em28xx_release_analog_resources(struct em28xx *dev);
/* Provided by em28xx-cards.c */
extern int em2800_variant_detect(struct usb_device *udev, int model);
-extern void em28xx_pre_card_setup(struct em28xx *dev);
-extern void em28xx_card_setup(struct em28xx *dev);
extern struct em28xx_board em28xx_boards[];
extern struct usb_device_id em28xx_id_table[];
extern const unsigned int em28xx_bcount;
-void em28xx_register_i2c_ir(struct em28xx *dev);
int em28xx_tuner_callback(void *ptr, int component, int command, int arg);
void em28xx_release_resources(struct em28xx *dev);
-/* Provided by em28xx-input.c */
-
-#ifdef CONFIG_VIDEO_EM28XX_RC
-
-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,
- u32 *ir_raw);
-int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key,
- u32 *ir_raw);
-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);
-
-#else
-
-#define em28xx_get_key_terratec NULL
-#define em28xx_get_key_em_haup NULL
-#define em28xx_get_key_pinnacle_usb_grey NULL
-#define em28xx_get_key_winfast_usbii_deluxe NULL
-
-static inline void em28xx_register_snapshot_button(struct em28xx *dev) {}
-static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {}
-static inline int em28xx_ir_init(struct em28xx *dev) { return 0; }
-static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; }
-
-#endif
-
/* Provided by em28xx-vbi.c */
extern struct videobuf_queue_ops em28xx_vbi_qops;
diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig
deleted file mode 100644
index 87981b078fe..00000000000
--- a/drivers/media/video/et61x251/Kconfig
+++ /dev/null
@@ -1,18 +0,0 @@
-config USB_ET61X251
- tristate "USB ET61X[12]51 PC Camera Controller support (DEPRECATED)"
- depends on VIDEO_V4L2
- default n
- ---help---
- This driver is DEPRECATED please use the gspca zc3xx module
- instead.
-
- Say Y here if you want support for cameras based on Etoms ET61X151
- or ET61X251 PC Camera Controllers.
-
- See <file:Documentation/video4linux/et61x251.txt> for more info.
-
- This driver uses the Video For Linux API. You must say Y or M to
- "Video For Linux" to use this driver.
-
- To compile this driver as a module, choose M here: the
- module will be called et61x251.
diff --git a/drivers/media/video/et61x251/Makefile b/drivers/media/video/et61x251/Makefile
deleted file mode 100644
index 2ff4db9ec88..00000000000
--- a/drivers/media/video/et61x251/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o
-
-obj-$(CONFIG_USB_ET61X251) += et61x251.o
-
diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h
deleted file mode 100644
index 337ded4a638..00000000000
--- a/drivers/media/video/et61x251/et61x251.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/***************************************************************************
- * V4L2 driver for ET61X[12]51 PC Camera Controllers *
- * *
- * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> *
- * *
- * 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 _ET61X251_H_
-#define _ET61X251_H_
-
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/time.h>
-#include <linux/wait.h>
-#include <linux/types.h>
-#include <linux/param.h>
-#include <linux/rwsem.h>
-#include <linux/mutex.h>
-#include <linux/stddef.h>
-#include <linux/string.h>
-#include <linux/kref.h>
-
-#include "et61x251_sensor.h"
-
-/*****************************************************************************/
-
-#define ET61X251_DEBUG
-#define ET61X251_DEBUG_LEVEL 2
-#define ET61X251_MAX_DEVICES 64
-#define ET61X251_PRESERVE_IMGSCALE 0
-#define ET61X251_FORCE_MUNMAP 0
-#define ET61X251_MAX_FRAMES 32
-#define ET61X251_COMPRESSION_QUALITY 0
-#define ET61X251_URBS 2
-#define ET61X251_ISO_PACKETS 7
-#define ET61X251_ALTERNATE_SETTING 13
-#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS)
-#define ET61X251_CTRL_TIMEOUT 100
-#define ET61X251_FRAME_TIMEOUT 2
-
-/*****************************************************************************/
-
-static const struct usb_device_id et61x251_id_table[] = {
- { USB_DEVICE(0x102c, 0x6251), },
- { }
-};
-
-ET61X251_SENSOR_TABLE
-
-/*****************************************************************************/
-
-enum et61x251_frame_state {
- F_UNUSED,
- F_QUEUED,
- F_GRABBING,
- F_DONE,
- F_ERROR,
-};
-
-struct et61x251_frame_t {
- void* bufmem;
- struct v4l2_buffer buf;
- enum et61x251_frame_state state;
- struct list_head frame;
- unsigned long vma_use_count;
-};
-
-enum et61x251_dev_state {
- DEV_INITIALIZED = 0x01,
- DEV_DISCONNECTED = 0x02,
- DEV_MISCONFIGURED = 0x04,
-};
-
-enum et61x251_io_method {
- IO_NONE,
- IO_READ,
- IO_MMAP,
-};
-
-enum et61x251_stream_state {
- STREAM_OFF,
- STREAM_INTERRUPT,
- STREAM_ON,
-};
-
-struct et61x251_sysfs_attr {
- u8 reg, i2c_reg;
-};
-
-struct et61x251_module_param {
- u8 force_munmap;
- u16 frame_timeout;
-};
-
-static DEFINE_MUTEX(et61x251_sysfs_lock);
-static DECLARE_RWSEM(et61x251_dev_lock);
-
-struct et61x251_device {
- struct video_device* v4ldev;
-
- struct et61x251_sensor sensor;
-
- struct usb_device* usbdev;
- struct urb* urb[ET61X251_URBS];
- void* transfer_buffer[ET61X251_URBS];
- u8* control_buffer;
-
- struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES];
- struct list_head inqueue, outqueue;
- u32 frame_count, nbuffers, nreadbuffers;
-
- enum et61x251_io_method io;
- enum et61x251_stream_state stream;
-
- struct v4l2_jpegcompression compression;
-
- struct et61x251_sysfs_attr sysfs;
- struct et61x251_module_param module_param;
-
- struct kref kref;
- enum et61x251_dev_state state;
- u8 users;
-
- struct completion probe;
- struct mutex open_mutex, fileop_mutex;
- spinlock_t queue_lock;
- wait_queue_head_t wait_open, wait_frame, wait_stream;
-};
-
-/*****************************************************************************/
-
-struct et61x251_device*
-et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id)
-{
- return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL;
-}
-
-
-void
-et61x251_attach_sensor(struct et61x251_device* cam,
- const struct et61x251_sensor* sensor)
-{
- memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor));
-}
-
-/*****************************************************************************/
-
-#undef DBG
-#undef KDBG
-#ifdef ET61X251_DEBUG
-#define DBG(level, fmt, ...) \
-do { \
- if (debug >= (level)) { \
- if ((level) == 1) \
- dev_err(&cam->usbdev->dev, fmt "\n", \
- ##__VA_ARGS__); \
- else if ((level) == 2) \
- dev_info(&cam->usbdev->dev, fmt "\n", \
- ##__VA_ARGS__); \
- else if ((level) >= 3) \
- dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
- __FILE__, __func__, __LINE__, \
- ##__VA_ARGS__); \
- } \
-} while (0)
-#define KDBG(level, fmt, ...) \
-do { \
- if (debug >= (level)) { \
- if ((level) == 1 || (level) == 2) \
- pr_info(fmt "\n", ##__VA_ARGS__); \
- else if ((level) == 3) \
- pr_debug("[%s:%s:%d] " fmt "\n", \
- __FILE__, __func__, __LINE__, \
- ##__VA_ARGS__); \
- } \
-} while (0)
-#define V4LDBG(level, name, cmd) \
-do { \
- if (debug >= (level)) \
- v4l_print_ioctl(name, cmd); \
-} while (0)
-#else
-#define DBG(level, fmt, ...) do {;} while(0)
-#define KDBG(level, fmt, ...) do {;} while(0)
-#define V4LDBG(level, name, cmd) do {;} while(0)
-#endif
-
-#undef PDBG
-#define PDBG(fmt, ...) \
- dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \
- __FILE__, __func__, __LINE__, ##__VA_ARGS__)
-
-#undef PDBGG
-#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */
-
-#endif /* _ET61X251_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c
deleted file mode 100644
index 5539f09440a..00000000000
--- a/drivers/media/video/et61x251/et61x251_core.c
+++ /dev/null
@@ -1,2683 +0,0 @@
-/***************************************************************************
- * V4L2 driver for ET61X[12]51 PC Camera Controllers *
- * *
- * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the Free Software *
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
- ***************************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/param.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/fs.h>
-#include <linux/delay.h>
-#include <linux/compiler.h>
-#include <linux/ioctl.h>
-#include <linux/poll.h>
-#include <linux/stat.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/page-flags.h>
-#include <media/v4l2-ioctl.h>
-#include <asm/byteorder.h>
-#include <asm/page.h>
-#include <asm/uaccess.h>
-
-#include "et61x251.h"
-
-/*****************************************************************************/
-
-#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \
- "PC Camera Controllers"
-#define ET61X251_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia"
-#define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>"
-#define ET61X251_MODULE_LICENSE "GPL"
-#define ET61X251_MODULE_VERSION "1.1.10"
-
-/*****************************************************************************/
-
-MODULE_DEVICE_TABLE(usb, et61x251_id_table);
-
-MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL);
-MODULE_DESCRIPTION(ET61X251_MODULE_NAME);
-MODULE_VERSION(ET61X251_MODULE_VERSION);
-MODULE_LICENSE(ET61X251_MODULE_LICENSE);
-
-static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1};
-module_param_array(video_nr, short, NULL, 0444);
-MODULE_PARM_DESC(video_nr,
- "\n<-1|n[,...]> Specify V4L2 minor mode number."
- "\n -1 = use next available (default)"
- "\n n = use minor number n (integer >= 0)"
- "\nYou can specify up to "
- __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way."
- "\nFor example:"
- "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
- "\nthe second registered camera and use auto for the first"
- "\none and for every other camera."
- "\n");
-
-static bool force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] =
- ET61X251_FORCE_MUNMAP};
-module_param_array(force_munmap, bool, NULL, 0444);
-MODULE_PARM_DESC(force_munmap,
- "\n<0|1[,...]> Force the application to unmap previously"
- "\nmapped buffer memory before calling any VIDIOC_S_CROP or"
- "\nVIDIOC_S_FMT ioctl's. Not all the applications support"
- "\nthis feature. This parameter is specific for each"
- "\ndetected camera."
- "\n 0 = do not force memory unmapping"
- "\n 1 = force memory unmapping (save memory)"
- "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"."
- "\n");
-
-static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] =
- ET61X251_FRAME_TIMEOUT};
-module_param_array(frame_timeout, uint, NULL, 0644);
-MODULE_PARM_DESC(frame_timeout,
- "\n<n[,...]> Timeout for a video frame in seconds."
- "\nThis parameter is specific for each detected camera."
- "\nDefault value is "
- __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"."
- "\n");
-
-#ifdef ET61X251_DEBUG
-static unsigned short debug = ET61X251_DEBUG_LEVEL;
-module_param(debug, ushort, 0644);
-MODULE_PARM_DESC(debug,
- "\n<n> Debugging information level, from 0 to 3:"
- "\n0 = none (use carefully)"
- "\n1 = critical errors"
- "\n2 = significant informations"
- "\n3 = more verbose messages"
- "\nLevel 3 is useful for testing only, when only "
- "one device is used."
- "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"."
- "\n");
-#endif
-
-/*****************************************************************************/
-
-static u32
-et61x251_request_buffers(struct et61x251_device* cam, u32 count,
- enum et61x251_io_method io)
-{
- struct v4l2_pix_format* p = &(cam->sensor.pix_format);
- struct v4l2_rect* r = &(cam->sensor.cropcap.bounds);
- const size_t imagesize = cam->module_param.force_munmap ||
- io == IO_READ ?
- (p->width * p->height * p->priv) / 8 :
- (r->width * r->height * p->priv) / 8;
- void* buff = NULL;
- u32 i;
-
- if (count > ET61X251_MAX_FRAMES)
- count = ET61X251_MAX_FRAMES;
-
- cam->nbuffers = count;
- while (cam->nbuffers > 0) {
- if ((buff = vmalloc_32_user(cam->nbuffers *
- PAGE_ALIGN(imagesize))))
- break;
- cam->nbuffers--;
- }
-
- for (i = 0; i < cam->nbuffers; i++) {
- cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize);
- cam->frame[i].buf.index = i;
- cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize);
- cam->frame[i].buf.length = imagesize;
- cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- cam->frame[i].buf.sequence = 0;
- cam->frame[i].buf.field = V4L2_FIELD_NONE;
- cam->frame[i].buf.memory = V4L2_MEMORY_MMAP;
- cam->frame[i].buf.flags = 0;
- }
-
- return cam->nbuffers;
-}
-
-
-static void et61x251_release_buffers(struct et61x251_device* cam)
-{
- if (cam->nbuffers) {
- vfree(cam->frame[0].bufmem);
- cam->nbuffers = 0;
- }
- cam->frame_current = NULL;
-}
-
-
-static void et61x251_empty_framequeues(struct et61x251_device* cam)
-{
- u32 i;
-
- INIT_LIST_HEAD(&cam->inqueue);
- INIT_LIST_HEAD(&cam->outqueue);
-
- for (i = 0; i < ET61X251_MAX_FRAMES; i++) {
- cam->frame[i].state = F_UNUSED;
- cam->frame[i].buf.bytesused = 0;
- }
-}
-
-
-static void et61x251_requeue_outqueue(struct et61x251_device* cam)
-{
- struct et61x251_frame_t *i;
-
- list_for_each_entry(i, &cam->outqueue, frame) {
- i->state = F_QUEUED;
- list_add(&i->frame, &cam->inqueue);
- }
-
- INIT_LIST_HEAD(&cam->outqueue);
-}
-
-
-static void et61x251_queue_unusedframes(struct et61x251_device* cam)
-{
- unsigned long lock_flags;
- u32 i;
-
- for (i = 0; i < cam->nbuffers; i++)
- if (cam->frame[i].state == F_UNUSED) {
- cam->frame[i].state = F_QUEUED;
- spin_lock_irqsave(&cam->queue_lock, lock_flags);
- list_add_tail(&cam->frame[i].frame, &cam->inqueue);
- spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
- }
-}
-
-/*****************************************************************************/
-
-int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index)
-{
- struct usb_device* udev = cam->usbdev;
- u8* buff = cam->control_buffer;
- int res;
-
- *buff = value;
-
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
- if (res < 0) {
- DBG(3, "Failed to write a register (value 0x%02X, index "
- "0x%02X, error %d)", value, index, res);
- return -1;
- }
-
- return 0;
-}
-
-
-static int et61x251_read_reg(struct et61x251_device* cam, u16 index)
-{
- struct usb_device* udev = cam->usbdev;
- u8* buff = cam->control_buffer;
- int res;
-
- res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
- 0, index, buff, 1, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- DBG(3, "Failed to read a register (index 0x%02X, error %d)",
- index, res);
-
- return (res >= 0) ? (int)(*buff) : -1;
-}
-
-
-static int
-et61x251_i2c_wait(struct et61x251_device* cam,
- const struct et61x251_sensor* sensor)
-{
- int i, r;
-
- for (i = 1; i <= 8; i++) {
- if (sensor->interface == ET61X251_I2C_3WIRES) {
- r = et61x251_read_reg(cam, 0x8e);
- if (!(r & 0x02) && (r >= 0))
- return 0;
- } else {
- r = et61x251_read_reg(cam, 0x8b);
- if (!(r & 0x01) && (r >= 0))
- return 0;
- }
- if (r < 0)
- return -EIO;
- udelay(8*8); /* minimum for sensors at 400kHz */
- }
-
- return -EBUSY;
-}
-
-
-int
-et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2,
- u8 data3, u8 data4, u8 data5, u8 data6, u8 data7,
- u8 data8, u8 address)
-{
- struct usb_device* udev = cam->usbdev;
- u8* data = cam->control_buffer;
- int err = 0, res;
-
- data[0] = data2;
- data[1] = data3;
- data[2] = data4;
- data[3] = data5;
- data[4] = data6;
- data[5] = data7;
- data[6] = data8;
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- data[0] = address;
- data[1] = cam->sensor.i2c_slave_id;
- data[2] = cam->sensor.rsta | 0x02 | (n << 4);
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- /* Start writing through the serial interface */
- data[0] = data1;
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- err += et61x251_i2c_wait(cam, &cam->sensor);
-
- if (err)
- DBG(3, "I2C raw write failed for %s image sensor",
- cam->sensor.name);
-
- PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, "
- "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X,"
- " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address,
- data1, data2, data3, data4, data5, data6, data7, data8);
-
- return err ? -1 : 0;
-
-}
-
-
-/*****************************************************************************/
-
-static void et61x251_urb_complete(struct urb *urb)
-{
- struct et61x251_device* cam = urb->context;
- struct et61x251_frame_t** f;
- size_t imagesize;
- u8 i;
- int err = 0;
-
- if (urb->status == -ENOENT)
- return;
-
- f = &cam->frame_current;
-
- if (cam->stream == STREAM_INTERRUPT) {
- cam->stream = STREAM_OFF;
- if ((*f))
- (*f)->state = F_QUEUED;
- DBG(3, "Stream interrupted");
- wake_up(&cam->wait_stream);
- }
-
- if (cam->state & DEV_DISCONNECTED)
- return;
-
- if (cam->state & DEV_MISCONFIGURED) {
- wake_up_interruptible(&cam->wait_frame);
- return;
- }
-
- if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue))
- goto resubmit_urb;
-
- if (!(*f))
- (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t,
- frame);
-
- imagesize = (cam->sensor.pix_format.width *
- cam->sensor.pix_format.height *
- cam->sensor.pix_format.priv) / 8;
-
- for (i = 0; i < urb->number_of_packets; i++) {
- unsigned int len, status;
- void *pos;
- u8* b1, * b2, sof;
- const u8 VOID_BYTES = 6;
- size_t imglen;
-
- len = urb->iso_frame_desc[i].actual_length;
- status = urb->iso_frame_desc[i].status;
- pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
-
- if (status) {
- DBG(3, "Error in isochronous frame");
- (*f)->state = F_ERROR;
- continue;
- }
-
- b1 = pos++;
- b2 = pos++;
- sof = ((*b1 & 0x3f) == 63);
- imglen = ((*b1 & 0xc0) << 2) | *b2;
-
- PDBGG("Isochrnous frame: length %u, #%u i, image length %zu",
- len, i, imglen);
-
- if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR)
-start_of_frame:
- if (sof) {
- (*f)->state = F_GRABBING;
- (*f)->buf.bytesused = 0;
- do_gettimeofday(&(*f)->buf.timestamp);
- pos += 22;
- DBG(3, "SOF detected: new video frame");
- }
-
- if ((*f)->state == F_GRABBING) {
- if (sof && (*f)->buf.bytesused) {
- if (cam->sensor.pix_format.pixelformat ==
- V4L2_PIX_FMT_ET61X251)
- goto end_of_frame;
- else {
- DBG(3, "Not expected SOF detected "
- "after %lu bytes",
- (unsigned long)(*f)->buf.bytesused);
- (*f)->state = F_ERROR;
- continue;
- }
- }
-
- if ((*f)->buf.bytesused + imglen > imagesize) {
- DBG(3, "Video frame size exceeded");
- (*f)->state = F_ERROR;
- continue;
- }
-
- pos += VOID_BYTES;
-
- memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen);
- (*f)->buf.bytesused += imglen;
-
- if ((*f)->buf.bytesused == imagesize) {
- u32 b;
-end_of_frame:
- b = (*f)->buf.bytesused;
- (*f)->state = F_DONE;
- (*f)->buf.sequence= ++cam->frame_count;
- spin_lock(&cam->queue_lock);
- list_move_tail(&(*f)->frame, &cam->outqueue);
- if (!list_empty(&cam->inqueue))
- (*f) = list_entry(cam->inqueue.next,
- struct et61x251_frame_t,
- frame);
- else
- (*f) = NULL;
- spin_unlock(&cam->queue_lock);
- DBG(3, "Video frame captured: : %lu bytes",
- (unsigned long)(b));
-
- if (!(*f))
- goto resubmit_urb;
-
- if (sof &&
- cam->sensor.pix_format.pixelformat ==
- V4L2_PIX_FMT_ET61X251)
- goto start_of_frame;
- }
- }
- }
-
-resubmit_urb:
- urb->dev = cam->usbdev;
- err = usb_submit_urb(urb, GFP_ATOMIC);
- if (err < 0 && err != -EPERM) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "usb_submit_urb() failed");
- }
-
- wake_up_interruptible(&cam->wait_frame);
-}
-
-
-static int et61x251_start_transfer(struct et61x251_device* cam)
-{
- struct usb_device *udev = cam->usbdev;
- struct urb* urb;
- struct usb_host_interface* altsetting = usb_altnum_to_altsetting(
- usb_ifnum_to_if(udev, 0),
- ET61X251_ALTERNATE_SETTING);
- const unsigned int psz = le16_to_cpu(altsetting->
- endpoint[0].desc.wMaxPacketSize);
- s8 i, j;
- int err = 0;
-
- for (i = 0; i < ET61X251_URBS; i++) {
- cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz,
- GFP_KERNEL);
- if (!cam->transfer_buffer[i]) {
- err = -ENOMEM;
- DBG(1, "Not enough memory");
- goto free_buffers;
- }
- }
-
- for (i = 0; i < ET61X251_URBS; i++) {
- urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL);
- cam->urb[i] = urb;
- if (!urb) {
- err = -ENOMEM;
- DBG(1, "usb_alloc_urb() failed");
- goto free_urbs;
- }
- urb->dev = udev;
- urb->context = cam;
- urb->pipe = usb_rcvisocpipe(udev, 1);
- urb->transfer_flags = URB_ISO_ASAP;
- urb->number_of_packets = ET61X251_ISO_PACKETS;
- urb->complete = et61x251_urb_complete;
- urb->transfer_buffer = cam->transfer_buffer[i];
- urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS;
- urb->interval = 1;
- for (j = 0; j < ET61X251_ISO_PACKETS; j++) {
- urb->iso_frame_desc[j].offset = psz * j;
- urb->iso_frame_desc[j].length = psz;
- }
- }
-
- err = et61x251_write_reg(cam, 0x01, 0x03);
- err = et61x251_write_reg(cam, 0x00, 0x03);
- err = et61x251_write_reg(cam, 0x08, 0x03);
- if (err) {
- err = -EIO;
- DBG(1, "I/O hardware error");
- goto free_urbs;
- }
-
- err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING);
- if (err) {
- DBG(1, "usb_set_interface() failed");
- goto free_urbs;
- }
-
- cam->frame_current = NULL;
-
- for (i = 0; i < ET61X251_URBS; i++) {
- err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
- if (err) {
- for (j = i-1; j >= 0; j--)
- usb_kill_urb(cam->urb[j]);
- DBG(1, "usb_submit_urb() failed, error %d", err);
- goto free_urbs;
- }
- }
-
- return 0;
-
-free_urbs:
- for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++)
- usb_free_urb(cam->urb[i]);
-
-free_buffers:
- for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++)
- kfree(cam->transfer_buffer[i]);
-
- return err;
-}
-
-
-static int et61x251_stop_transfer(struct et61x251_device* cam)
-{
- struct usb_device *udev = cam->usbdev;
- s8 i;
- int err = 0;
-
- if (cam->state & DEV_DISCONNECTED)
- return 0;
-
- for (i = ET61X251_URBS-1; i >= 0; i--) {
- usb_kill_urb(cam->urb[i]);
- usb_free_urb(cam->urb[i]);
- kfree(cam->transfer_buffer[i]);
- }
-
- err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */
- if (err)
- DBG(3, "usb_set_interface() failed");
-
- return err;
-}
-
-
-static int et61x251_stream_interrupt(struct et61x251_device* cam)
-{
- long timeout;
-
- cam->stream = STREAM_INTERRUPT;
- timeout = wait_event_timeout(cam->wait_stream,
- (cam->stream == STREAM_OFF) ||
- (cam->state & DEV_DISCONNECTED),
- ET61X251_URB_TIMEOUT);
- if (cam->state & DEV_DISCONNECTED)
- return -ENODEV;
- else if (cam->stream != STREAM_OFF) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "URB timeout reached. The camera is misconfigured. To "
- "use it, close and open %s again.",
- video_device_node_name(cam->v4ldev));
- return -EIO;
- }
-
- return 0;
-}
-
-/*****************************************************************************/
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-
-static int et61x251_i2c_try_read(struct et61x251_device* cam,
- const struct et61x251_sensor* sensor,
- u8 address)
-{
- struct usb_device* udev = cam->usbdev;
- u8* data = cam->control_buffer;
- int err = 0, res;
-
- data[0] = address;
- data[1] = cam->sensor.i2c_slave_id;
- data[2] = cam->sensor.rsta | 0x10;
- data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02);
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- err += et61x251_i2c_wait(cam, sensor);
-
- res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1,
- 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- if (err)
- DBG(3, "I2C read failed for %s image sensor", sensor->name);
-
- PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]);
-
- return err ? -1 : (int)data[0];
-}
-
-
-static int et61x251_i2c_try_write(struct et61x251_device* cam,
- const struct et61x251_sensor* sensor,
- u8 address, u8 value)
-{
- struct usb_device* udev = cam->usbdev;
- u8* data = cam->control_buffer;
- int err = 0, res;
-
- data[0] = address;
- data[1] = cam->sensor.i2c_slave_id;
- data[2] = cam->sensor.rsta | 0x12;
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- data[0] = value;
- res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41,
- 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT);
- if (res < 0)
- err += res;
-
- err += et61x251_i2c_wait(cam, sensor);
-
- if (err)
- DBG(3, "I2C write failed for %s image sensor", sensor->name);
-
- PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value);
-
- return err ? -1 : 0;
-}
-
-static int et61x251_i2c_read(struct et61x251_device* cam, u8 address)
-{
- return et61x251_i2c_try_read(cam, &cam->sensor, address);
-}
-
-static int et61x251_i2c_write(struct et61x251_device* cam,
- u8 address, u8 value)
-{
- return et61x251_i2c_try_write(cam, &cam->sensor, address, value);
-}
-
-static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count)
-{
- char str[5];
- char* endp;
- unsigned long val;
-
- if (len < 4) {
- strncpy(str, buff, len);
- str[len] = '\0';
- } else {
- strncpy(str, buff, 4);
- str[4] = '\0';
- }
-
- val = simple_strtoul(str, &endp, 0);
-
- *count = 0;
- if (val <= 0xff)
- *count = (ssize_t)(endp - str);
- if ((*count) && (len == *count+1) && (buff[*count] == '\n'))
- *count += 1;
-
- return (u8)val;
-}
-
-/*
- NOTE 1: being inside one of the following methods implies that the v4l
- device exists for sure (see kobjects and reference counters)
- NOTE 2: buffers are PAGE_SIZE long
-*/
-
-static ssize_t et61x251_show_reg(struct device* cd,
- struct device_attribute *attr, char* buf)
-{
- struct et61x251_device* cam;
- ssize_t count;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- count = sprintf(buf, "%u\n", cam->sysfs.reg);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static ssize_t
-et61x251_store_reg(struct device* cd,
- struct device_attribute *attr, const char* buf, size_t len)
-{
- struct et61x251_device* cam;
- u8 index;
- ssize_t count;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- index = et61x251_strtou8(buf, len, &count);
- if (index > 0x8e || !count) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EINVAL;
- }
-
- cam->sysfs.reg = index;
-
- DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg);
- DBG(3, "Written bytes: %zd", count);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static ssize_t et61x251_show_val(struct device* cd,
- struct device_attribute *attr, char* buf)
-{
- struct et61x251_device* cam;
- ssize_t count;
- int val;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EIO;
- }
-
- count = sprintf(buf, "%d\n", val);
-
- DBG(3, "Read bytes: %zd", count);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static ssize_t
-et61x251_store_val(struct device* cd, struct device_attribute *attr,
- const char* buf, size_t len)
-{
- struct et61x251_device* cam;
- u8 value;
- ssize_t count;
- int err;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- value = et61x251_strtou8(buf, len, &count);
- if (!count) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EINVAL;
- }
-
- err = et61x251_write_reg(cam, value, cam->sysfs.reg);
- if (err) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EIO;
- }
-
- DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X",
- cam->sysfs.reg, value);
- DBG(3, "Written bytes: %zd", count);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static ssize_t et61x251_show_i2c_reg(struct device* cd,
- struct device_attribute *attr, char* buf)
-{
- struct et61x251_device* cam;
- ssize_t count;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg);
-
- DBG(3, "Read bytes: %zd", count);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static ssize_t
-et61x251_store_i2c_reg(struct device* cd, struct device_attribute *attr,
- const char* buf, size_t len)
-{
- struct et61x251_device* cam;
- u8 index;
- ssize_t count;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- index = et61x251_strtou8(buf, len, &count);
- if (!count) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EINVAL;
- }
-
- cam->sysfs.i2c_reg = index;
-
- DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg);
- DBG(3, "Written bytes: %zd", count);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static ssize_t et61x251_show_i2c_val(struct device* cd,
- struct device_attribute *attr, char* buf)
-{
- struct et61x251_device* cam;
- ssize_t count;
- int val;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENOSYS;
- }
-
- if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EIO;
- }
-
- count = sprintf(buf, "%d\n", val);
-
- DBG(3, "Read bytes: %zd", count);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static ssize_t
-et61x251_store_i2c_val(struct device* cd, struct device_attribute *attr,
- const char* buf, size_t len)
-{
- struct et61x251_device* cam;
- u8 value;
- ssize_t count;
- int err;
-
- if (mutex_lock_interruptible(&et61x251_sysfs_lock))
- return -ERESTARTSYS;
-
- cam = video_get_drvdata(to_video_device(cd));
- if (!cam) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENODEV;
- }
-
- if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -ENOSYS;
- }
-
- value = et61x251_strtou8(buf, len, &count);
- if (!count) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EINVAL;
- }
-
- err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value);
- if (err) {
- mutex_unlock(&et61x251_sysfs_lock);
- return -EIO;
- }
-
- DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X",
- cam->sysfs.i2c_reg, value);
- DBG(3, "Written bytes: %zd", count);
-
- mutex_unlock(&et61x251_sysfs_lock);
-
- return count;
-}
-
-
-static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR,
- et61x251_show_reg, et61x251_store_reg);
-static DEVICE_ATTR(val, S_IRUGO | S_IWUSR,
- et61x251_show_val, et61x251_store_val);
-static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR,
- et61x251_show_i2c_reg, et61x251_store_i2c_reg);
-static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR,
- et61x251_show_i2c_val, et61x251_store_i2c_val);
-
-
-static int et61x251_create_sysfs(struct et61x251_device* cam)
-{
- struct device *classdev = &(cam->v4ldev->dev);
- int err = 0;
-
- if ((err = device_create_file(classdev, &dev_attr_reg)))
- goto err_out;
- if ((err = device_create_file(classdev, &dev_attr_val)))
- goto err_reg;
-
- if (cam->sensor.sysfs_ops) {
- if ((err = device_create_file(classdev, &dev_attr_i2c_reg)))
- goto err_val;
- if ((err = device_create_file(classdev, &dev_attr_i2c_val)))
- goto err_i2c_reg;
- }
-
-err_i2c_reg:
- if (cam->sensor.sysfs_ops)
- device_remove_file(classdev, &dev_attr_i2c_reg);
-err_val:
- device_remove_file(classdev, &dev_attr_val);
-err_reg:
- device_remove_file(classdev, &dev_attr_reg);
-err_out:
- return err;
-}
-#endif /* CONFIG_VIDEO_ADV_DEBUG */
-
-/*****************************************************************************/
-
-static int
-et61x251_set_pix_format(struct et61x251_device* cam,
- struct v4l2_pix_format* pix)
-{
- int r, err = 0;
-
- if ((r = et61x251_read_reg(cam, 0x12)) < 0)
- err += r;
- if (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
- err += et61x251_write_reg(cam, r & 0xfd, 0x12);
- else
- err += et61x251_write_reg(cam, r | 0x02, 0x12);
-
- return err ? -EIO : 0;
-}
-
-
-static int
-et61x251_set_compression(struct et61x251_device* cam,
- struct v4l2_jpegcompression* compression)
-{
- int r, err = 0;
-
- if ((r = et61x251_read_reg(cam, 0x12)) < 0)
- err += r;
- if (compression->quality == 0)
- err += et61x251_write_reg(cam, r & 0xfb, 0x12);
- else
- err += et61x251_write_reg(cam, r | 0x04, 0x12);
-
- return err ? -EIO : 0;
-}
-
-
-static int et61x251_set_scale(struct et61x251_device* cam, u8 scale)
-{
- int r = 0, err = 0;
-
- r = et61x251_read_reg(cam, 0x12);
- if (r < 0)
- err += r;
-
- if (scale == 1)
- err += et61x251_write_reg(cam, r & ~0x01, 0x12);
- else if (scale == 2)
- err += et61x251_write_reg(cam, r | 0x01, 0x12);
-
- if (err)
- return -EIO;
-
- PDBGG("Scaling factor: %u", scale);
-
- return 0;
-}
-
-
-static int
-et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect)
-{
- struct et61x251_sensor* s = &cam->sensor;
- u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left +
- s->active_pixel.left),
- fmw_sy = (u16)(rect->top - s->cropcap.bounds.top +
- s->active_pixel.top),
- fmw_length = (u16)(rect->width),
- fmw_height = (u16)(rect->height);
- int err = 0;
-
- err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69);
- err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a);
- err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b);
- err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c);
- err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6)
- | ((fmw_length & 0x300) >> 4)
- | ((fmw_height & 0x300) >> 2), 0x6d);
- if (err)
- return -EIO;
-
- PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u",
- fmw_sx, fmw_sy, fmw_length, fmw_height);
-
- return 0;
-}
-
-
-static int et61x251_init(struct et61x251_device* cam)
-{
- struct et61x251_sensor* s = &cam->sensor;
- struct v4l2_control ctrl;
- struct v4l2_queryctrl *qctrl;
- struct v4l2_rect* rect;
- u8 i = 0;
- int err = 0;
-
- if (!(cam->state & DEV_INITIALIZED)) {
- mutex_init(&cam->open_mutex);
- init_waitqueue_head(&cam->wait_open);
- qctrl = s->qctrl;
- rect = &(s->cropcap.defrect);
- cam->compression.quality = ET61X251_COMPRESSION_QUALITY;
- } else { /* use current values */
- qctrl = s->_qctrl;
- rect = &(s->_rect);
- }
-
- err += et61x251_set_scale(cam, rect->width / s->pix_format.width);
- err += et61x251_set_crop(cam, rect);
- if (err)
- return err;
-
- if (s->init) {
- err = s->init(cam);
- if (err) {
- DBG(3, "Sensor initialization failed");
- return err;
- }
- }
-
- err += et61x251_set_compression(cam, &cam->compression);
- err += et61x251_set_pix_format(cam, &s->pix_format);
- if (s->set_pix_format)
- err += s->set_pix_format(cam, &s->pix_format);
- if (err)
- return err;
-
- if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251)
- DBG(3, "Compressed video format is active, quality %d",
- cam->compression.quality);
- else
- DBG(3, "Uncompressed video format is active");
-
- if (s->set_crop)
- if ((err = s->set_crop(cam, rect))) {
- DBG(3, "set_crop() failed");
- return err;
- }
-
- if (s->set_ctrl) {
- for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
- if (s->qctrl[i].id != 0 &&
- !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) {
- ctrl.id = s->qctrl[i].id;
- ctrl.value = qctrl[i].default_value;
- err = s->set_ctrl(cam, &ctrl);
- if (err) {
- DBG(3, "Set %s control failed",
- s->qctrl[i].name);
- return err;
- }
- DBG(3, "Image sensor supports '%s' control",
- s->qctrl[i].name);
- }
- }
-
- if (!(cam->state & DEV_INITIALIZED)) {
- mutex_init(&cam->fileop_mutex);
- spin_lock_init(&cam->queue_lock);
- init_waitqueue_head(&cam->wait_frame);
- init_waitqueue_head(&cam->wait_stream);
- cam->nreadbuffers = 2;
- memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl));
- memcpy(&(s->_rect), &(s->cropcap.defrect),
- sizeof(struct v4l2_rect));
- cam->state |= DEV_INITIALIZED;
- }
-
- DBG(2, "Initialization succeeded");
- return 0;
-}
-
-/*****************************************************************************/
-
-static void et61x251_release_resources(struct kref *kref)
-{
- struct et61x251_device *cam;
-
- mutex_lock(&et61x251_sysfs_lock);
-
- cam = container_of(kref, struct et61x251_device, kref);
-
- DBG(2, "V4L2 device %s deregistered",
- video_device_node_name(cam->v4ldev));
- video_set_drvdata(cam->v4ldev, NULL);
- video_unregister_device(cam->v4ldev);
- usb_put_dev(cam->usbdev);
- kfree(cam->control_buffer);
- kfree(cam);
-
- mutex_unlock(&et61x251_sysfs_lock);
-}
-
-
-static int et61x251_open(struct file *filp)
-{
- struct et61x251_device* cam;
- int err = 0;
-
- if (!down_read_trylock(&et61x251_dev_lock))
- return -ERESTARTSYS;
-
- cam = video_drvdata(filp);
-
- if (wait_for_completion_interruptible(&cam->probe)) {
- up_read(&et61x251_dev_lock);
- return -ERESTARTSYS;
- }
-
- kref_get(&cam->kref);
-
- if (mutex_lock_interruptible(&cam->open_mutex)) {
- kref_put(&cam->kref, et61x251_release_resources);
- up_read(&et61x251_dev_lock);
- return -ERESTARTSYS;
- }
-
- if (cam->state & DEV_DISCONNECTED) {
- DBG(1, "Device not present");
- err = -ENODEV;
- goto out;
- }
-
- if (cam->users) {
- DBG(2, "Device %s is already in use",
- video_device_node_name(cam->v4ldev));
- DBG(3, "Simultaneous opens are not supported");
- if ((filp->f_flags & O_NONBLOCK) ||
- (filp->f_flags & O_NDELAY)) {
- err = -EWOULDBLOCK;
- goto out;
- }
- DBG(2, "A blocking open() has been requested. Wait for the "
- "device to be released...");
- up_read(&et61x251_dev_lock);
- err = wait_event_interruptible_exclusive(cam->wait_open,
- (cam->state & DEV_DISCONNECTED)
- || !cam->users);
- down_read(&et61x251_dev_lock);
- if (err)
- goto out;
- if (cam->state & DEV_DISCONNECTED) {
- err = -ENODEV;
- goto out;
- }
- }
-
- if (cam->state & DEV_MISCONFIGURED) {
- err = et61x251_init(cam);
- if (err) {
- DBG(1, "Initialization failed again. "
- "I will retry on next open().");
- goto out;
- }
- cam->state &= ~DEV_MISCONFIGURED;
- }
-
- if ((err = et61x251_start_transfer(cam)))
- goto out;
-
- filp->private_data = cam;
- cam->users++;
- cam->io = IO_NONE;
- cam->stream = STREAM_OFF;
- cam->nbuffers = 0;
- cam->frame_count = 0;
- et61x251_empty_framequeues(cam);
-
- DBG(3, "Video device %s is open",
- video_device_node_name(cam->v4ldev));
-
-out:
- mutex_unlock(&cam->open_mutex);
- if (err)
- kref_put(&cam->kref, et61x251_release_resources);
- up_read(&et61x251_dev_lock);
- return err;
-}
-
-
-static int et61x251_release(struct file *filp)
-{
- struct et61x251_device* cam;
-
- down_write(&et61x251_dev_lock);
-
- cam = video_drvdata(filp);
-
- et61x251_stop_transfer(cam);
- et61x251_release_buffers(cam);
- cam->users--;
- wake_up_interruptible_nr(&cam->wait_open, 1);
-
- DBG(3, "Video device %s closed",
- video_device_node_name(cam->v4ldev));
-
- kref_put(&cam->kref, et61x251_release_resources);
-
- up_write(&et61x251_dev_lock);
-
- return 0;
-}
-
-
-static ssize_t
-et61x251_read(struct file* filp, char __user * buf,
- size_t count, loff_t* f_pos)
-{
- struct et61x251_device *cam = video_drvdata(filp);
- struct et61x251_frame_t* f, * i;
- unsigned long lock_flags;
- long timeout;
- int err = 0;
-
- if (mutex_lock_interruptible(&cam->fileop_mutex))
- return -ERESTARTSYS;
-
- if (cam->state & DEV_DISCONNECTED) {
- DBG(1, "Device not present");
- mutex_unlock(&cam->fileop_mutex);
- return -ENODEV;
- }
-
- if (cam->state & DEV_MISCONFIGURED) {
- DBG(1, "The camera is misconfigured. Close and open it "
- "again.");
- mutex_unlock(&cam->fileop_mutex);
- return -EIO;
- }
-
- if (cam->io == IO_MMAP) {
- DBG(3, "Close and open the device again to choose the read "
- "method");
- mutex_unlock(&cam->fileop_mutex);
- return -EBUSY;
- }
-
- if (cam->io == IO_NONE) {
- if (!et61x251_request_buffers(cam, cam->nreadbuffers,
- IO_READ)) {
- DBG(1, "read() failed, not enough memory");
- mutex_unlock(&cam->fileop_mutex);
- return -ENOMEM;
- }
- cam->io = IO_READ;
- cam->stream = STREAM_ON;
- }
-
- if (list_empty(&cam->inqueue)) {
- if (!list_empty(&cam->outqueue))
- et61x251_empty_framequeues(cam);
- et61x251_queue_unusedframes(cam);
- }
-
- if (!count) {
- mutex_unlock(&cam->fileop_mutex);
- return 0;
- }
-
- if (list_empty(&cam->outqueue)) {
- if (filp->f_flags & O_NONBLOCK) {
- mutex_unlock(&cam->fileop_mutex);
- return -EAGAIN;
- }
- timeout = wait_event_interruptible_timeout
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED),
- msecs_to_jiffies(
- cam->module_param.frame_timeout * 1000
- )
- );
- if (timeout < 0) {
- mutex_unlock(&cam->fileop_mutex);
- return timeout;
- }
- if (cam->state & DEV_DISCONNECTED) {
- mutex_unlock(&cam->fileop_mutex);
- return -ENODEV;
- }
- if (!timeout || (cam->state & DEV_MISCONFIGURED)) {
- mutex_unlock(&cam->fileop_mutex);
- return -EIO;
- }
- }
-
- f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame);
-
- if (count > f->buf.bytesused)
- count = f->buf.bytesused;
-
- if (copy_to_user(buf, f->bufmem, count)) {
- err = -EFAULT;
- goto exit;
- }
- *f_pos += count;
-
-exit:
- spin_lock_irqsave(&cam->queue_lock, lock_flags);
- list_for_each_entry(i, &cam->outqueue, frame)
- i->state = F_UNUSED;
- INIT_LIST_HEAD(&cam->outqueue);
- spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
-
- et61x251_queue_unusedframes(cam);
-
- PDBGG("Frame #%lu, bytes read: %zu",
- (unsigned long)f->buf.index, count);
-
- mutex_unlock(&cam->fileop_mutex);
-
- return err ? err : count;
-}
-
-
-static unsigned int et61x251_poll(struct file *filp, poll_table *wait)
-{
- struct et61x251_device *cam = video_drvdata(filp);
- struct et61x251_frame_t* f;
- unsigned long lock_flags;
- unsigned int mask = 0;
-
- if (mutex_lock_interruptible(&cam->fileop_mutex))
- return POLLERR;
-
- if (cam->state & DEV_DISCONNECTED) {
- DBG(1, "Device not present");
- goto error;
- }
-
- if (cam->state & DEV_MISCONFIGURED) {
- DBG(1, "The camera is misconfigured. Close and open it "
- "again.");
- goto error;
- }
-
- if (cam->io == IO_NONE) {
- if (!et61x251_request_buffers(cam, cam->nreadbuffers,
- IO_READ)) {
- DBG(1, "poll() failed, not enough memory");
- goto error;
- }
- cam->io = IO_READ;
- cam->stream = STREAM_ON;
- }
-
- if (cam->io == IO_READ) {
- spin_lock_irqsave(&cam->queue_lock, lock_flags);
- list_for_each_entry(f, &cam->outqueue, frame)
- f->state = F_UNUSED;
- INIT_LIST_HEAD(&cam->outqueue);
- spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
- et61x251_queue_unusedframes(cam);
- }
-
- poll_wait(filp, &cam->wait_frame, wait);
-
- if (!list_empty(&cam->outqueue))
- mask |= POLLIN | POLLRDNORM;
-
- mutex_unlock(&cam->fileop_mutex);
-
- return mask;
-
-error:
- mutex_unlock(&cam->fileop_mutex);
- return POLLERR;
-}
-
-
-static void et61x251_vm_open(struct vm_area_struct* vma)
-{
- struct et61x251_frame_t* f = vma->vm_private_data;
- f->vma_use_count++;
-}
-
-
-static void et61x251_vm_close(struct vm_area_struct* vma)
-{
- /* NOTE: buffers are not freed here */
- struct et61x251_frame_t* f = vma->vm_private_data;
- f->vma_use_count--;
-}
-
-
-static const struct vm_operations_struct et61x251_vm_ops = {
- .open = et61x251_vm_open,
- .close = et61x251_vm_close,
-};
-
-
-static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma)
-{
- struct et61x251_device *cam = video_drvdata(filp);
- unsigned long size = vma->vm_end - vma->vm_start,
- start = vma->vm_start;
- void *pos;
- u32 i;
-
- if (mutex_lock_interruptible(&cam->fileop_mutex))
- return -ERESTARTSYS;
-
- if (cam->state & DEV_DISCONNECTED) {
- DBG(1, "Device not present");
- mutex_unlock(&cam->fileop_mutex);
- return -ENODEV;
- }
-
- if (cam->state & DEV_MISCONFIGURED) {
- DBG(1, "The camera is misconfigured. Close and open it "
- "again.");
- mutex_unlock(&cam->fileop_mutex);
- return -EIO;
- }
-
- if (!(vma->vm_flags & (VM_WRITE | VM_READ))) {
- mutex_unlock(&cam->fileop_mutex);
- return -EACCES;
- }
-
- if (cam->io != IO_MMAP ||
- size != PAGE_ALIGN(cam->frame[0].buf.length)) {
- mutex_unlock(&cam->fileop_mutex);
- return -EINVAL;
- }
-
- for (i = 0; i < cam->nbuffers; i++) {
- if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff)
- break;
- }
- if (i == cam->nbuffers) {
- mutex_unlock(&cam->fileop_mutex);
- return -EINVAL;
- }
-
- vma->vm_flags |= VM_IO;
- vma->vm_flags |= VM_RESERVED;
-
- pos = cam->frame[i].bufmem;
- while (size > 0) { /* size is page-aligned */
- if (vm_insert_page(vma, start, vmalloc_to_page(pos))) {
- mutex_unlock(&cam->fileop_mutex);
- return -EAGAIN;
- }
- start += PAGE_SIZE;
- pos += PAGE_SIZE;
- size -= PAGE_SIZE;
- }
-
- vma->vm_ops = &et61x251_vm_ops;
- vma->vm_private_data = &cam->frame[i];
- et61x251_vm_open(vma);
-
- mutex_unlock(&cam->fileop_mutex);
-
- return 0;
-}
-
-/*****************************************************************************/
-
-static int
-et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_capability cap = {
- .driver = "et61x251",
- .version = LINUX_VERSION_CODE,
- .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING,
- };
-
- 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, dev_name(&cam->usbdev->dev),
- sizeof(cap.bus_info));
-
- if (copy_to_user(arg, &cap, sizeof(cap)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_input i;
-
- if (copy_from_user(&i, arg, sizeof(i)))
- return -EFAULT;
-
- if (i.index)
- return -EINVAL;
-
- memset(&i, 0, sizeof(i));
- strcpy(i.name, "Camera");
- i.type = V4L2_INPUT_TYPE_CAMERA;
- i.capabilities = V4L2_IN_CAP_STD;
-
- if (copy_to_user(arg, &i, sizeof(i)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg)
-{
- int index = 0;
-
- if (copy_to_user(arg, &index, sizeof(index)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg)
-{
- int index;
-
- if (copy_from_user(&index, arg, sizeof(index)))
- return -EFAULT;
-
- if (index != 0)
- return -EINVAL;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg)
-{
- struct et61x251_sensor* s = &cam->sensor;
- struct v4l2_queryctrl qc;
- u8 i;
-
- if (copy_from_user(&qc, arg, sizeof(qc)))
- return -EFAULT;
-
- for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
- if (qc.id && qc.id == s->qctrl[i].id) {
- memcpy(&qc, &(s->qctrl[i]), sizeof(qc));
- if (copy_to_user(arg, &qc, sizeof(qc)))
- return -EFAULT;
- return 0;
- }
-
- return -EINVAL;
-}
-
-
-static int
-et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg)
-{
- struct et61x251_sensor* s = &cam->sensor;
- struct v4l2_control ctrl;
- int err = 0;
- u8 i;
-
- if (!s->get_ctrl && !s->set_ctrl)
- return -EINVAL;
-
- if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
- return -EFAULT;
-
- if (!s->get_ctrl) {
- for (i = 0; i < ARRAY_SIZE(s->qctrl); i++)
- if (ctrl.id == s->qctrl[i].id) {
- ctrl.value = s->_qctrl[i].default_value;
- goto exit;
- }
- return -EINVAL;
- } else
- err = s->get_ctrl(cam, &ctrl);
-
-exit:
- if (copy_to_user(arg, &ctrl, sizeof(ctrl)))
- return -EFAULT;
-
- return err;
-}
-
-
-static int
-et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg)
-{
- struct et61x251_sensor* s = &cam->sensor;
- struct v4l2_control ctrl;
- u8 i;
- int err = 0;
-
- if (!s->set_ctrl)
- return -EINVAL;
-
- if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
- return -EFAULT;
-
- for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) {
- if (ctrl.id == s->qctrl[i].id) {
- if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)
- return -EINVAL;
- if (ctrl.value < s->qctrl[i].minimum ||
- ctrl.value > s->qctrl[i].maximum)
- return -ERANGE;
- ctrl.value -= ctrl.value % s->qctrl[i].step;
- break;
- }
- }
- if (i == ARRAY_SIZE(s->qctrl))
- return -EINVAL;
- if ((err = s->set_ctrl(cam, &ctrl)))
- return err;
-
- s->_qctrl[i].default_value = ctrl.value;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_cropcap* cc = &(cam->sensor.cropcap);
-
- cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- cc->pixelaspect.numerator = 1;
- cc->pixelaspect.denominator = 1;
-
- if (copy_to_user(arg, cc, sizeof(*cc)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg)
-{
- struct et61x251_sensor* s = &cam->sensor;
- struct v4l2_crop crop = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- };
-
- memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect));
-
- if (copy_to_user(arg, &crop, sizeof(crop)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg)
-{
- struct et61x251_sensor* s = &cam->sensor;
- struct v4l2_crop crop;
- struct v4l2_rect* rect;
- struct v4l2_rect* bounds = &(s->cropcap.bounds);
- struct v4l2_pix_format* pix_format = &(s->pix_format);
- u8 scale;
- const enum et61x251_stream_state stream = cam->stream;
- const u32 nbuffers = cam->nbuffers;
- u32 i;
- int err = 0;
-
- if (copy_from_user(&crop, arg, sizeof(crop)))
- return -EFAULT;
-
- rect = &(crop.c);
-
- if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (cam->module_param.force_munmap)
- for (i = 0; i < cam->nbuffers; i++)
- if (cam->frame[i].vma_use_count) {
- DBG(3, "VIDIOC_S_CROP failed. "
- "Unmap the buffers first.");
- return -EBUSY;
- }
-
- /* Preserve R,G or B origin */
- rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L;
- rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L;
-
- if (rect->width < 16)
- rect->width = 16;
- if (rect->height < 16)
- rect->height = 16;
- if (rect->width > bounds->width)
- rect->width = bounds->width;
- if (rect->height > bounds->height)
- rect->height = bounds->height;
- if (rect->left < bounds->left)
- rect->left = bounds->left;
- if (rect->top < bounds->top)
- rect->top = bounds->top;
- if (rect->left + rect->width > bounds->left + bounds->width)
- rect->left = bounds->left+bounds->width - rect->width;
- if (rect->top + rect->height > bounds->top + bounds->height)
- rect->top = bounds->top+bounds->height - rect->height;
-
- rect->width &= ~15L;
- rect->height &= ~15L;
-
- if (ET61X251_PRESERVE_IMGSCALE) {
- /* Calculate the actual scaling factor */
- u32 a, b;
- a = rect->width * rect->height;
- b = pix_format->width * pix_format->height;
- scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
- } else
- scale = 1;
-
- if (cam->stream == STREAM_ON)
- if ((err = et61x251_stream_interrupt(cam)))
- return err;
-
- if (copy_to_user(arg, &crop, sizeof(crop))) {
- cam->stream = stream;
- return -EFAULT;
- }
-
- if (cam->module_param.force_munmap || cam->io == IO_READ)
- et61x251_release_buffers(cam);
-
- err = et61x251_set_crop(cam, rect);
- if (s->set_crop)
- err += s->set_crop(cam, rect);
- err += et61x251_set_scale(cam, scale);
-
- if (err) { /* atomic, no rollback in ioctl() */
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To "
- "use the camera, close and open %s again.",
- video_device_node_name(cam->v4ldev));
- return -EIO;
- }
-
- s->pix_format.width = rect->width/scale;
- s->pix_format.height = rect->height/scale;
- memcpy(&(s->_rect), rect, sizeof(*rect));
-
- if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
- nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To "
- "use the camera, close and open %s again.",
- video_device_node_name(cam->v4ldev));
- return -ENOMEM;
- }
-
- if (cam->io == IO_READ)
- et61x251_empty_framequeues(cam);
- else if (cam->module_param.force_munmap)
- et61x251_requeue_outqueue(cam);
-
- cam->stream = stream;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_frmsizeenum frmsize;
-
- if (copy_from_user(&frmsize, arg, sizeof(frmsize)))
- return -EFAULT;
-
- if (frmsize.index != 0)
- return -EINVAL;
-
- if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 &&
- frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8)
- return -EINVAL;
-
- frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE;
- frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16;
- frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16;
- frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width;
- frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height;
- memset(&frmsize.reserved, 0, sizeof(frmsize.reserved));
-
- if (copy_to_user(arg, &frmsize, sizeof(frmsize)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_fmtdesc fmtd;
-
- if (copy_from_user(&fmtd, arg, sizeof(fmtd)))
- return -EFAULT;
-
- if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- if (fmtd.index == 0) {
- strcpy(fmtd.description, "bayer rgb");
- fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8;
- } else if (fmtd.index == 1) {
- strcpy(fmtd.description, "compressed");
- fmtd.pixelformat = V4L2_PIX_FMT_ET61X251;
- fmtd.flags = V4L2_FMT_FLAG_COMPRESSED;
- } else
- return -EINVAL;
-
- fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- memset(&fmtd.reserved, 0, sizeof(fmtd.reserved));
-
- if (copy_to_user(arg, &fmtd, sizeof(fmtd)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_format format;
- struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format);
-
- if (copy_from_user(&format, arg, sizeof(format)))
- return -EFAULT;
-
- if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ?
- 0 : V4L2_COLORSPACE_SRGB;
- pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251)
- ? 0 : (pfmt->width * pfmt->priv) / 8;
- pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8);
- pfmt->field = V4L2_FIELD_NONE;
- memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt));
-
- if (copy_to_user(arg, &format, sizeof(format)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd,
- void __user * arg)
-{
- struct et61x251_sensor* s = &cam->sensor;
- struct v4l2_format format;
- struct v4l2_pix_format* pix;
- struct v4l2_pix_format* pfmt = &(s->pix_format);
- struct v4l2_rect* bounds = &(s->cropcap.bounds);
- struct v4l2_rect rect;
- u8 scale;
- const enum et61x251_stream_state stream = cam->stream;
- const u32 nbuffers = cam->nbuffers;
- u32 i;
- int err = 0;
-
- if (copy_from_user(&format, arg, sizeof(format)))
- return -EFAULT;
-
- pix = &(format.fmt.pix);
-
- if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- memcpy(&rect, &(s->_rect), sizeof(rect));
-
- { /* calculate the actual scaling factor */
- u32 a, b;
- a = rect.width * rect.height;
- b = pix->width * pix->height;
- scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
- }
-
- rect.width = scale * pix->width;
- rect.height = scale * pix->height;
-
- if (rect.width < 16)
- rect.width = 16;
- if (rect.height < 16)
- rect.height = 16;
- if (rect.width > bounds->left + bounds->width - rect.left)
- rect.width = bounds->left + bounds->width - rect.left;
- if (rect.height > bounds->top + bounds->height - rect.top)
- rect.height = bounds->top + bounds->height - rect.top;
-
- rect.width &= ~15L;
- rect.height &= ~15L;
-
- { /* adjust the scaling factor */
- u32 a, b;
- a = rect.width * rect.height;
- b = pix->width * pix->height;
- scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1;
- }
-
- pix->width = rect.width / scale;
- pix->height = rect.height / scale;
-
- if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 &&
- pix->pixelformat != V4L2_PIX_FMT_SBGGR8)
- pix->pixelformat = pfmt->pixelformat;
- pix->priv = pfmt->priv; /* bpp */
- pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ?
- 0 : V4L2_COLORSPACE_SRGB;
- pix->colorspace = pfmt->colorspace;
- pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251)
- ? 0 : (pix->width * pix->priv) / 8;
- pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8);
- pix->field = V4L2_FIELD_NONE;
-
- if (cmd == VIDIOC_TRY_FMT) {
- if (copy_to_user(arg, &format, sizeof(format)))
- return -EFAULT;
- return 0;
- }
-
- if (cam->module_param.force_munmap)
- for (i = 0; i < cam->nbuffers; i++)
- if (cam->frame[i].vma_use_count) {
- DBG(3, "VIDIOC_S_FMT failed. "
- "Unmap the buffers first.");
- return -EBUSY;
- }
-
- if (cam->stream == STREAM_ON)
- if ((err = et61x251_stream_interrupt(cam)))
- return err;
-
- if (copy_to_user(arg, &format, sizeof(format))) {
- cam->stream = stream;
- return -EFAULT;
- }
-
- if (cam->module_param.force_munmap || cam->io == IO_READ)
- et61x251_release_buffers(cam);
-
- err += et61x251_set_pix_format(cam, pix);
- err += et61x251_set_crop(cam, &rect);
- if (s->set_pix_format)
- err += s->set_pix_format(cam, pix);
- if (s->set_crop)
- err += s->set_crop(cam, &rect);
- err += et61x251_set_scale(cam, scale);
-
- if (err) { /* atomic, no rollback in ioctl() */
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To "
- "use the camera, close and open %s again.",
- video_device_node_name(cam->v4ldev));
- return -EIO;
- }
-
- memcpy(pfmt, pix, sizeof(*pix));
- memcpy(&(s->_rect), &rect, sizeof(rect));
-
- if ((cam->module_param.force_munmap || cam->io == IO_READ) &&
- nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) {
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To "
- "use the camera, close and open %s again.",
- video_device_node_name(cam->v4ldev));
- return -ENOMEM;
- }
-
- if (cam->io == IO_READ)
- et61x251_empty_framequeues(cam);
- else if (cam->module_param.force_munmap)
- et61x251_requeue_outqueue(cam);
-
- cam->stream = stream;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg)
-{
- if (copy_to_user(arg, &cam->compression,
- sizeof(cam->compression)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_jpegcompression jc;
- const enum et61x251_stream_state stream = cam->stream;
- int err = 0;
-
- if (copy_from_user(&jc, arg, sizeof(jc)))
- return -EFAULT;
-
- if (jc.quality != 0 && jc.quality != 1)
- return -EINVAL;
-
- if (cam->stream == STREAM_ON)
- if ((err = et61x251_stream_interrupt(cam)))
- return err;
-
- err += et61x251_set_compression(cam, &jc);
- if (err) { /* atomic, no rollback in ioctl() */
- cam->state |= DEV_MISCONFIGURED;
- DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware "
- "problems. To use the camera, close and open "
- "%s again.", video_device_node_name(cam->v4ldev));
- return -EIO;
- }
-
- cam->compression.quality = jc.quality;
-
- cam->stream = stream;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_requestbuffers rb;
- u32 i;
- int err;
-
- if (copy_from_user(&rb, arg, sizeof(rb)))
- return -EFAULT;
-
- if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- rb.memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
-
- if (cam->io == IO_READ) {
- DBG(3, "Close and open the device again to choose the mmap "
- "I/O method");
- return -EBUSY;
- }
-
- for (i = 0; i < cam->nbuffers; i++)
- if (cam->frame[i].vma_use_count) {
- DBG(3, "VIDIOC_REQBUFS failed. "
- "Previous buffers are still mapped.");
- return -EBUSY;
- }
-
- if (cam->stream == STREAM_ON)
- if ((err = et61x251_stream_interrupt(cam)))
- return err;
-
- et61x251_empty_framequeues(cam);
-
- et61x251_release_buffers(cam);
- if (rb.count)
- rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP);
-
- if (copy_to_user(arg, &rb, sizeof(rb))) {
- et61x251_release_buffers(cam);
- cam->io = IO_NONE;
- return -EFAULT;
- }
-
- cam->io = rb.count ? IO_MMAP : IO_NONE;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_buffer b;
-
- if (copy_from_user(&b, arg, sizeof(b)))
- return -EFAULT;
-
- if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- b.index >= cam->nbuffers || cam->io != IO_MMAP)
- return -EINVAL;
-
- memcpy(&b, &cam->frame[b.index].buf, sizeof(b));
-
- if (cam->frame[b.index].vma_use_count)
- b.flags |= V4L2_BUF_FLAG_MAPPED;
-
- if (cam->frame[b.index].state == F_DONE)
- b.flags |= V4L2_BUF_FLAG_DONE;
- else if (cam->frame[b.index].state != F_UNUSED)
- b.flags |= V4L2_BUF_FLAG_QUEUED;
-
- if (copy_to_user(arg, &b, sizeof(b)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_buffer b;
- unsigned long lock_flags;
-
- if (copy_from_user(&b, arg, sizeof(b)))
- return -EFAULT;
-
- if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
- b.index >= cam->nbuffers || cam->io != IO_MMAP)
- return -EINVAL;
-
- if (cam->frame[b.index].state != F_UNUSED)
- return -EINVAL;
-
- cam->frame[b.index].state = F_QUEUED;
-
- spin_lock_irqsave(&cam->queue_lock, lock_flags);
- list_add_tail(&cam->frame[b.index].frame, &cam->inqueue);
- spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
-
- PDBGG("Frame #%lu queued", (unsigned long)b.index);
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp,
- void __user * arg)
-{
- struct v4l2_buffer b;
- struct et61x251_frame_t *f;
- unsigned long lock_flags;
- long timeout;
-
- if (copy_from_user(&b, arg, sizeof(b)))
- return -EFAULT;
-
- if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP)
- return -EINVAL;
-
- if (list_empty(&cam->outqueue)) {
- if (cam->stream == STREAM_OFF)
- return -EINVAL;
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
- timeout = wait_event_interruptible_timeout
- ( cam->wait_frame,
- (!list_empty(&cam->outqueue)) ||
- (cam->state & DEV_DISCONNECTED) ||
- (cam->state & DEV_MISCONFIGURED),
- cam->module_param.frame_timeout *
- 1000 * msecs_to_jiffies(1) );
- if (timeout < 0)
- return timeout;
- if (cam->state & DEV_DISCONNECTED)
- return -ENODEV;
- if (!timeout || (cam->state & DEV_MISCONFIGURED))
- return -EIO;
- }
-
- spin_lock_irqsave(&cam->queue_lock, lock_flags);
- f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame);
- list_del(cam->outqueue.next);
- spin_unlock_irqrestore(&cam->queue_lock, lock_flags);
-
- f->state = F_UNUSED;
-
- memcpy(&b, &f->buf, sizeof(b));
- if (f->vma_use_count)
- b.flags |= V4L2_BUF_FLAG_MAPPED;
-
- if (copy_to_user(arg, &b, sizeof(b)))
- return -EFAULT;
-
- PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index);
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg)
-{
- int type;
-
- if (copy_from_user(&type, arg, sizeof(type)))
- return -EFAULT;
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
- return -EINVAL;
-
- cam->stream = STREAM_ON;
-
- DBG(3, "Stream on");
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg)
-{
- int type, err;
-
- if (copy_from_user(&type, arg, sizeof(type)))
- return -EFAULT;
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP)
- return -EINVAL;
-
- if (cam->stream == STREAM_ON)
- if ((err = et61x251_stream_interrupt(cam)))
- return err;
-
- et61x251_empty_framequeues(cam);
-
- DBG(3, "Stream off");
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_streamparm sp;
-
- if (copy_from_user(&sp, arg, sizeof(sp)))
- return -EFAULT;
-
- if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- sp.parm.capture.extendedmode = 0;
- sp.parm.capture.readbuffers = cam->nreadbuffers;
-
- if (copy_to_user(arg, &sp, sizeof(sp)))
- return -EFAULT;
-
- return 0;
-}
-
-
-static int
-et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg)
-{
- struct v4l2_streamparm sp;
-
- if (copy_from_user(&sp, arg, sizeof(sp)))
- return -EFAULT;
-
- if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- sp.parm.capture.extendedmode = 0;
-
- if (sp.parm.capture.readbuffers == 0)
- sp.parm.capture.readbuffers = cam->nreadbuffers;
-
- if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES)
- sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES;
-
- if (copy_to_user(arg, &sp, sizeof(sp)))
- return -EFAULT;
-
- cam->nreadbuffers = sp.parm.capture.readbuffers;
-
- return 0;
-}
-
-
-static long et61x251_ioctl_v4l2(struct file *filp,
- unsigned int cmd, void __user *arg)
-{
- struct et61x251_device *cam = video_drvdata(filp);
-
- switch (cmd) {
-
- case VIDIOC_QUERYCAP:
- return et61x251_vidioc_querycap(cam, arg);
-
- case VIDIOC_ENUMINPUT:
- return et61x251_vidioc_enuminput(cam, arg);
-
- case VIDIOC_G_INPUT:
- return et61x251_vidioc_g_input(cam, arg);
-
- case VIDIOC_S_INPUT:
- return et61x251_vidioc_s_input(cam, arg);
-
- case VIDIOC_QUERYCTRL:
- return et61x251_vidioc_query_ctrl(cam, arg);
-
- case VIDIOC_G_CTRL:
- return et61x251_vidioc_g_ctrl(cam, arg);
-
- case VIDIOC_S_CTRL:
- return et61x251_vidioc_s_ctrl(cam, arg);
-
- case VIDIOC_CROPCAP:
- return et61x251_vidioc_cropcap(cam, arg);
-
- case VIDIOC_G_CROP:
- return et61x251_vidioc_g_crop(cam, arg);
-
- case VIDIOC_S_CROP:
- return et61x251_vidioc_s_crop(cam, arg);
-
- case VIDIOC_ENUM_FMT:
- return et61x251_vidioc_enum_fmt(cam, arg);
-
- case VIDIOC_G_FMT:
- return et61x251_vidioc_g_fmt(cam, arg);
-
- case VIDIOC_TRY_FMT:
- case VIDIOC_S_FMT:
- return et61x251_vidioc_try_s_fmt(cam, cmd, arg);
-
- case VIDIOC_ENUM_FRAMESIZES:
- return et61x251_vidioc_enum_framesizes(cam, arg);
-
- case VIDIOC_G_JPEGCOMP:
- return et61x251_vidioc_g_jpegcomp(cam, arg);
-
- case VIDIOC_S_JPEGCOMP:
- return et61x251_vidioc_s_jpegcomp(cam, arg);
-
- case VIDIOC_REQBUFS:
- return et61x251_vidioc_reqbufs(cam, arg);
-
- case VIDIOC_QUERYBUF:
- return et61x251_vidioc_querybuf(cam, arg);
-
- case VIDIOC_QBUF:
- return et61x251_vidioc_qbuf(cam, arg);
-
- case VIDIOC_DQBUF:
- return et61x251_vidioc_dqbuf(cam, filp, arg);
-
- case VIDIOC_STREAMON:
- return et61x251_vidioc_streamon(cam, arg);
-
- case VIDIOC_STREAMOFF:
- return et61x251_vidioc_streamoff(cam, arg);
-
- case VIDIOC_G_PARM:
- return et61x251_vidioc_g_parm(cam, arg);
-
- case VIDIOC_S_PARM:
- return et61x251_vidioc_s_parm(cam, arg);
-
- default:
- return -ENOTTY;
-
- }
-}
-
-
-static long et61x251_ioctl(struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- struct et61x251_device *cam = video_drvdata(filp);
- long err = 0;
-
- if (mutex_lock_interruptible(&cam->fileop_mutex))
- return -ERESTARTSYS;
-
- if (cam->state & DEV_DISCONNECTED) {
- DBG(1, "Device not present");
- mutex_unlock(&cam->fileop_mutex);
- return -ENODEV;
- }
-
- if (cam->state & DEV_MISCONFIGURED) {
- DBG(1, "The camera is misconfigured. Close and open it "
- "again.");
- mutex_unlock(&cam->fileop_mutex);
- return -EIO;
- }
-
- V4LDBG(3, "et61x251", cmd);
-
- err = et61x251_ioctl_v4l2(filp, cmd, (void __user *)arg);
-
- mutex_unlock(&cam->fileop_mutex);
-
- return err;
-}
-
-
-static const struct v4l2_file_operations et61x251_fops = {
- .owner = THIS_MODULE,
- .open = et61x251_open,
- .release = et61x251_release,
- .unlocked_ioctl = et61x251_ioctl,
- .read = et61x251_read,
- .poll = et61x251_poll,
- .mmap = et61x251_mmap,
-};
-
-/*****************************************************************************/
-
-/* It exists a single interface only. We do not need to validate anything. */
-static int
-et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
-{
- struct usb_device *udev = interface_to_usbdev(intf);
- struct et61x251_device* cam;
- static unsigned int dev_nr;
- unsigned int i;
- int err = 0;
-
- if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL)))
- return -ENOMEM;
-
- cam->usbdev = udev;
-
- if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) {
- DBG(1, "kmalloc() failed");
- err = -ENOMEM;
- goto fail;
- }
-
- if (!(cam->v4ldev = video_device_alloc())) {
- DBG(1, "video_device_alloc() failed");
- err = -ENOMEM;
- goto fail;
- }
-
- DBG(2, "ET61X[12]51 PC Camera Controller detected "
- "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct);
-
- for (i = 0; et61x251_sensor_table[i]; i++) {
- err = et61x251_sensor_table[i](cam);
- if (!err)
- break;
- }
-
- if (!err)
- DBG(2, "%s image sensor detected", cam->sensor.name);
- else {
- DBG(1, "No supported image sensor detected");
- err = -ENODEV;
- goto fail;
- }
-
- if (et61x251_init(cam)) {
- DBG(1, "Initialization failed. I will retry on open().");
- cam->state |= DEV_MISCONFIGURED;
- }
-
- strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera");
- cam->v4ldev->fops = &et61x251_fops;
- cam->v4ldev->release = video_device_release;
- cam->v4ldev->parent = &udev->dev;
- video_set_drvdata(cam->v4ldev, cam);
-
- init_completion(&cam->probe);
-
- err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
- video_nr[dev_nr]);
- if (err) {
- DBG(1, "V4L2 device registration failed");
- if (err == -ENFILE && video_nr[dev_nr] == -1)
- DBG(1, "Free /dev/videoX node not found");
- video_nr[dev_nr] = -1;
- dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
- complete_all(&cam->probe);
- goto fail;
- }
-
- DBG(2, "V4L2 device registered as %s",
- video_device_node_name(cam->v4ldev));
-
- cam->module_param.force_munmap = force_munmap[dev_nr];
- cam->module_param.frame_timeout = frame_timeout[dev_nr];
-
- dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0;
-
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- err = et61x251_create_sysfs(cam);
- if (!err)
- DBG(2, "Optional device control through 'sysfs' "
- "interface ready");
- else
- DBG(2, "Failed to create 'sysfs' interface for optional "
- "device controlling. Error #%d", err);
-#else
- DBG(2, "Optional device control through 'sysfs' interface disabled");
- DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' "
- "configuration option to enable it.");
-#endif
-
- usb_set_intfdata(intf, cam);
- kref_init(&cam->kref);
- usb_get_dev(cam->usbdev);
-
- complete_all(&cam->probe);
-
- return 0;
-
-fail:
- if (cam) {
- kfree(cam->control_buffer);
- if (cam->v4ldev)
- video_device_release(cam->v4ldev);
- kfree(cam);
- }
- return err;
-}
-
-
-static void et61x251_usb_disconnect(struct usb_interface* intf)
-{
- struct et61x251_device* cam;
-
- down_write(&et61x251_dev_lock);
-
- cam = usb_get_intfdata(intf);
-
- DBG(2, "Disconnecting %s...", cam->v4ldev->name);
-
- if (cam->users) {
- DBG(2, "Device %s is open! Deregistration and memory "
- "deallocation are deferred.",
- video_device_node_name(cam->v4ldev));
- cam->state |= DEV_MISCONFIGURED;
- et61x251_stop_transfer(cam);
- cam->state |= DEV_DISCONNECTED;
- wake_up_interruptible(&cam->wait_frame);
- wake_up(&cam->wait_stream);
- } else
- cam->state |= DEV_DISCONNECTED;
-
- wake_up_interruptible_all(&cam->wait_open);
-
- kref_put(&cam->kref, et61x251_release_resources);
-
- up_write(&et61x251_dev_lock);
-}
-
-
-static struct usb_driver et61x251_usb_driver = {
- .name = "et61x251",
- .id_table = et61x251_id_table,
- .probe = et61x251_usb_probe,
- .disconnect = et61x251_usb_disconnect,
-};
-
-module_usb_driver(et61x251_usb_driver);
diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h
deleted file mode 100644
index 71a03148cb0..00000000000
--- a/drivers/media/video/et61x251/et61x251_sensor.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/***************************************************************************
- * API for image sensors connected to ET61X[12]51 PC Camera Controllers *
- * *
- * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
- * *
- * 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 _ET61X251_SENSOR_H_
-#define _ET61X251_SENSOR_H_
-
-#include <linux/usb.h>
-#include <linux/videodev2.h>
-#include <linux/device.h>
-#include <linux/stddef.h>
-#include <linux/errno.h>
-#include <asm/types.h>
-
-struct et61x251_device;
-struct et61x251_sensor;
-
-/*****************************************************************************/
-
-extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam);
-
-#define ET61X251_SENSOR_TABLE \
-/* Weak detections must go at the end of the list */ \
-static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \
- &et61x251_probe_tas5130d1b, \
- NULL, \
-};
-
-extern struct et61x251_device*
-et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id);
-
-extern void
-et61x251_attach_sensor(struct et61x251_device* cam,
- const struct et61x251_sensor* sensor);
-
-/*****************************************************************************/
-
-extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index);
-extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1,
- u8 data2, u8 data3, u8 data4, u8 data5,
- u8 data6, u8 data7, u8 data8, u8 address);
-
-/*****************************************************************************/
-
-enum et61x251_i2c_sysfs_ops {
- ET61X251_I2C_READ = 0x01,
- ET61X251_I2C_WRITE = 0x02,
-};
-
-enum et61x251_i2c_interface {
- ET61X251_I2C_2WIRES,
- ET61X251_I2C_3WIRES,
-};
-
-/* Repeat start condition when RSTA is high */
-enum et61x251_i2c_rsta {
- ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */
- ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */
-};
-
-#define ET61X251_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10)
-
-struct et61x251_sensor {
- char name[32];
-
- enum et61x251_i2c_sysfs_ops sysfs_ops;
-
- enum et61x251_i2c_interface interface;
- u8 i2c_slave_id;
- enum et61x251_i2c_rsta rsta;
- struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */
-
- struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS];
- struct v4l2_cropcap cropcap;
- struct v4l2_pix_format pix_format;
-
- int (*init)(struct et61x251_device* cam);
- int (*get_ctrl)(struct et61x251_device* cam,
- struct v4l2_control* ctrl);
- int (*set_ctrl)(struct et61x251_device* cam,
- const struct v4l2_control* ctrl);
- int (*set_crop)(struct et61x251_device* cam,
- const struct v4l2_rect* rect);
- int (*set_pix_format)(struct et61x251_device* cam,
- const struct v4l2_pix_format* pix);
-
- /* Private */
- struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS];
- struct v4l2_rect _rect;
-};
-
-#endif /* _ET61X251_SENSOR_H_ */
diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c
deleted file mode 100644
index ced2e167935..00000000000
--- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/***************************************************************************
- * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 *
- * PC Camera Controllers *
- * *
- * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program; if not, write to the Free Software *
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
- ***************************************************************************/
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include "et61x251_sensor.h"
-
-
-static int tas5130d1b_init(struct et61x251_device* cam)
-{
- int err = 0;
-
- err += et61x251_write_reg(cam, 0x14, 0x01);
- err += et61x251_write_reg(cam, 0x1b, 0x02);
- err += et61x251_write_reg(cam, 0x02, 0x12);
- err += et61x251_write_reg(cam, 0x0e, 0x60);
- err += et61x251_write_reg(cam, 0x80, 0x61);
- err += et61x251_write_reg(cam, 0xf0, 0x62);
- err += et61x251_write_reg(cam, 0x03, 0x63);
- err += et61x251_write_reg(cam, 0x14, 0x64);
- err += et61x251_write_reg(cam, 0xf4, 0x65);
- err += et61x251_write_reg(cam, 0x01, 0x66);
- err += et61x251_write_reg(cam, 0x05, 0x67);
- err += et61x251_write_reg(cam, 0x8f, 0x68);
- err += et61x251_write_reg(cam, 0x0f, 0x8d);
- err += et61x251_write_reg(cam, 0x08, 0x8e);
-
- return err;
-}
-
-
-static int tas5130d1b_set_ctrl(struct et61x251_device* cam,
- const struct v4l2_control* ctrl)
-{
- int err = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_GAIN:
- err += et61x251_i2c_raw_write(cam, 2, 0x20,
- 0xf6-ctrl->value, 0, 0, 0,
- 0, 0, 0, 0);
- break;
- case V4L2_CID_EXPOSURE:
- err += et61x251_i2c_raw_write(cam, 2, 0x40,
- 0x47-ctrl->value, 0, 0, 0,
- 0, 0, 0, 0);
- break;
- default:
- return -EINVAL;
- }
-
- return err ? -EIO : 0;
-}
-
-
-static const struct et61x251_sensor tas5130d1b = {
- .name = "TAS5130D1B",
- .interface = ET61X251_I2C_3WIRES,
- .rsta = ET61X251_I2C_RSTA_STOP,
- .active_pixel = {
- .left = 106,
- .top = 13,
- },
- .init = &tas5130d1b_init,
- .qctrl = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "global gain",
- .minimum = 0x00,
- .maximum = 0xf6,
- .step = 0x02,
- .default_value = 0x0d,
- .flags = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "exposure",
- .minimum = 0x00,
- .maximum = 0x47,
- .step = 0x01,
- .default_value = 0x23,
- .flags = 0,
- },
- },
- .set_ctrl = &tas5130d1b_set_ctrl,
- .cropcap = {
- .bounds = {
- .left = 0,
- .top = 0,
- .width = 640,
- .height = 480,
- },
- .defrect = {
- .left = 0,
- .top = 0,
- .width = 640,
- .height = 480,
- },
- },
- .pix_format = {
- .width = 640,
- .height = 480,
- .pixelformat = V4L2_PIX_FMT_SBGGR8,
- .priv = 8,
- },
-};
-
-
-int et61x251_probe_tas5130d1b(struct et61x251_device* cam)
-{
- const struct usb_device_id tas5130d1b_id_table[] = {
- { USB_DEVICE(0x102c, 0x6251), },
- { }
- };
-
- /* Sensor detection is based on USB pid/vid */
- if (!et61x251_match_id(cam, tas5130d1b_id_table))
- return -ENODEV;
-
- et61x251_attach_sensor(cam, &tas5130d1b);
-
- return 0;
-}
diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c
index 27e3e0c0b21..777486f7cad 100644
--- a/drivers/media/video/fsl-viu.c
+++ b/drivers/media/video/fsl-viu.c
@@ -1544,6 +1544,10 @@ static int __devinit viu_of_probe(struct platform_device *op)
/* initialize locks */
mutex_init(&viu_dev->lock);
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &viu_dev->vdev->flags);
viu_dev->vdev->lock = &viu_dev->lock;
spin_lock_init(&viu_dev->slock);
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
index 79ebe46e1ad..c901da0bd65 100644
--- a/drivers/media/video/gspca/Makefile
+++ b/drivers/media/video/gspca/Makefile
@@ -43,7 +43,7 @@ obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o
obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o
obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
-gspca_main-objs := gspca.o
+gspca_main-objs := gspca.o autogain_functions.o
gspca_benq-objs := benq.o
gspca_conex-objs := conex.o
gspca_cpia1-objs := cpia1.o
diff --git a/drivers/media/video/gspca/autogain_functions.c b/drivers/media/video/gspca/autogain_functions.c
new file mode 100644
index 00000000000..67db674bb04
--- /dev/null
+++ b/drivers/media/video/gspca/autogain_functions.c
@@ -0,0 +1,178 @@
+/*
+ * Functions for auto gain.
+ *
+ * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "gspca.h"
+
+/* auto gain and exposure algorithm based on the knee algorithm described here:
+ http://ytse.tricolour.net/docs/LowLightOptimization.html
+
+ Returns 0 if no changes were made, 1 if the gain and or exposure settings
+ where changed. */
+int gspca_expo_autogain(
+ struct gspca_dev *gspca_dev,
+ int avg_lum,
+ int desired_avg_lum,
+ int deadzone,
+ int gain_knee,
+ int exposure_knee)
+{
+ s32 gain, orig_gain, exposure, orig_exposure;
+ int i, steps, retval = 0;
+
+ if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
+ return 0;
+
+ orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
+ orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+ /* If we are of a multiple of deadzone, do multiple steps to reach the
+ 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",
+ avg_lum, desired_avg_lum, steps);
+
+ for (i = 0; i < steps; i++) {
+ if (avg_lum > desired_avg_lum) {
+ if (gain > gain_knee)
+ gain--;
+ else if (exposure > exposure_knee)
+ exposure--;
+ else if (gain > gspca_dev->gain->default_value)
+ gain--;
+ else if (exposure > gspca_dev->exposure->minimum)
+ exposure--;
+ else if (gain > gspca_dev->gain->minimum)
+ gain--;
+ else
+ break;
+ } else {
+ if (gain < gspca_dev->gain->default_value)
+ gain++;
+ else if (exposure < exposure_knee)
+ exposure++;
+ else if (gain < gain_knee)
+ gain++;
+ else if (exposure < gspca_dev->exposure->maximum)
+ exposure++;
+ else if (gain < gspca_dev->gain->maximum)
+ gain++;
+ else
+ break;
+ }
+ }
+
+ if (gain != orig_gain) {
+ v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
+ retval = 1;
+ }
+ if (exposure != orig_exposure) {
+ v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
+ retval = 1;
+ }
+
+ if (retval)
+ PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
+ gain, exposure);
+ return retval;
+}
+EXPORT_SYMBOL(gspca_expo_autogain);
+
+/* Autogain + exposure algorithm for cameras with a coarse exposure control
+ (usually this means we can only control the clockdiv to change exposure)
+ As changing the clockdiv so that the fps drops from 30 to 15 fps for
+ example, will lead to a huge exposure change (it effectively doubles),
+ this algorithm normally tries to only adjust the gain (between 40 and
+ 80 %) and if that does not help, only then changes exposure. This leads
+ to a much more stable image then using the knee algorithm which at
+ certain points of the knee graph will only try to adjust exposure,
+ which leads to oscilating as one exposure step is huge.
+
+ Returns 0 if no changes were made, 1 if the gain and or exposure settings
+ where changed. */
+int gspca_coarse_grained_expo_autogain(
+ struct gspca_dev *gspca_dev,
+ int avg_lum,
+ int desired_avg_lum,
+ int deadzone)
+{
+ s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure;
+ int steps, retval = 0;
+
+ if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
+ return 0;
+
+ orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
+ orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
+
+ gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
+ 5 * 2 + gspca_dev->gain->minimum;
+ gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
+ 5 * 4 + gspca_dev->gain->minimum;
+
+ /* If we are of a multiple of deadzone, do multiple steps to reach the
+ desired lumination fast (with the risc of a slight overshoot) */
+ steps = (desired_avg_lum - avg_lum) / deadzone;
+
+ PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
+ avg_lum, desired_avg_lum, steps);
+
+ if ((gain + steps) > gain_high &&
+ exposure < gspca_dev->exposure->maximum) {
+ gain = gain_high;
+ gspca_dev->exp_too_low_cnt++;
+ gspca_dev->exp_too_high_cnt = 0;
+ } else if ((gain + steps) < gain_low &&
+ exposure > gspca_dev->exposure->minimum) {
+ gain = gain_low;
+ gspca_dev->exp_too_high_cnt++;
+ gspca_dev->exp_too_low_cnt = 0;
+ } else {
+ gain += steps;
+ if (gain > gspca_dev->gain->maximum)
+ gain = gspca_dev->gain->maximum;
+ else if (gain < gspca_dev->gain->minimum)
+ gain = gspca_dev->gain->minimum;
+ gspca_dev->exp_too_high_cnt = 0;
+ gspca_dev->exp_too_low_cnt = 0;
+ }
+
+ if (gspca_dev->exp_too_high_cnt > 3) {
+ exposure--;
+ gspca_dev->exp_too_high_cnt = 0;
+ } else if (gspca_dev->exp_too_low_cnt > 3) {
+ exposure++;
+ gspca_dev->exp_too_low_cnt = 0;
+ }
+
+ if (gain != orig_gain) {
+ v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
+ retval = 1;
+ }
+ if (exposure != orig_exposure) {
+ v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
+ retval = 1;
+ }
+
+ if (retval)
+ PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
+ gain, exposure);
+ return retval;
+}
+EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain);
diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/video/gspca/autogain_functions.h
index 46777eee678..d625eafe63e 100644
--- a/drivers/media/video/gspca/autogain_functions.h
+++ b/drivers/media/video/gspca/autogain_functions.h
@@ -18,6 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#ifdef WANT_REGULAR_AUTOGAIN
/* auto gain and exposure algorithm based on the knee algorithm described here:
http://ytse.tricolour.net/docs/LowLightOptimization.html
@@ -91,7 +92,9 @@ static inline int auto_gain_n_exposure(
gain, exposure);
return retval;
}
+#endif
+#ifdef WANT_COARSE_EXPO_AUTOGAIN
/* Autogain + exposure algorithm for cameras with a coarse exposure control
(usually this means we can only control the clockdiv to change exposure)
As changing the clockdiv so that the fps drops from 30 to 15 fps for
@@ -103,7 +106,7 @@ static inline int auto_gain_n_exposure(
which leads to oscilating as one exposure step is huge.
Note this assumes that the sd struct for the cam in question has
- exp_too_high_cnt and exp_too_high_cnt int members for use by this function.
+ exp_too_low_cnt and exp_too_high_cnt int members for use by this function.
Returns 0 if no changes were made, 1 if the gain and or exposure settings
where changed. */
@@ -177,3 +180,4 @@ static inline int coarse_grained_expo_autogain(
gain, exposure);
return retval;
}
+#endif
diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
index ea17b5d94ea..f39fee0fd10 100644
--- a/drivers/media/video/gspca/conex.c
+++ b/drivers/media/video/gspca/conex.c
@@ -306,7 +306,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev)
reg_w(gspca_dev, 0x0020, reg20, 8);
reg_w(gspca_dev, 0x0028, reg28, 8);
- reg_w(gspca_dev, 0x0010, reg10, 8);
+ reg_w(gspca_dev, 0x0010, reg10, 2);
reg_w_val(gspca_dev, 0x0092, 0x03);
switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
@@ -326,7 +326,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev)
}
reg_w(gspca_dev, 0x007b, reg7b, 6);
reg_w_val(gspca_dev, 0x00f8, 0x00);
- reg_w(gspca_dev, 0x0010, reg10, 8);
+ reg_w(gspca_dev, 0x0010, reg10, 2);
reg_w_val(gspca_dev, 0x0098, 0x41);
for (i = 0; i < 11; i++) {
if (i == 3 || i == 5 || i == 8)
diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
index 0107513cd72..6e26c93b465 100644
--- a/drivers/media/video/gspca/finepix.c
+++ b/drivers/media/video/gspca/finepix.c
@@ -94,7 +94,11 @@ static void dostream(struct work_struct *work)
/* loop reading a frame */
again:
- while (gspca_dev->present && gspca_dev->streaming) {
+ while (gspca_dev->dev && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
/* request a frame */
mutex_lock(&gspca_dev->usb_lock);
@@ -102,7 +106,11 @@ again:
mutex_unlock(&gspca_dev->usb_lock);
if (ret < 0)
break;
- if (!gspca_dev->present || !gspca_dev->streaming)
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
+ if (!gspca_dev->dev || !gspca_dev->streaming)
break;
/* the frame comes in parts */
@@ -117,7 +125,11 @@ again:
* error. Just restart. */
goto again;
}
- if (!gspca_dev->present || !gspca_dev->streaming)
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ goto out;
+#endif
+ if (!gspca_dev->dev || !gspca_dev->streaming)
goto out;
if (len < FPIX_MAX_TRANSFER ||
(data[len - 2] == 0xff &&
diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
index c84e26006fc..c549574c1c7 100644
--- a/drivers/media/video/gspca/gl860/gl860.c
+++ b/drivers/media/video/gspca/gl860/gl860.c
@@ -405,6 +405,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ if (!sd->gspca_dev.present)
+ return;
+
return sd->dev_post_unset_alt(gspca_dev);
}
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
index ca5a2b139d0..137166d7394 100644
--- a/drivers/media/video/gspca/gspca.c
+++ b/drivers/media/video/gspca/gspca.c
@@ -38,6 +38,9 @@
#include <linux/uaccess.h>
#include <linux/ktime.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
#include "gspca.h"
@@ -592,16 +595,13 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
static void gspca_stream_off(struct gspca_dev *gspca_dev)
{
gspca_dev->streaming = 0;
- if (gspca_dev->present) {
- if (gspca_dev->sd_desc->stopN)
- gspca_dev->sd_desc->stopN(gspca_dev);
- destroy_urbs(gspca_dev);
- gspca_input_destroy_urb(gspca_dev);
- gspca_set_alt0(gspca_dev);
- gspca_input_create_urb(gspca_dev);
- }
-
- /* always call stop0 to free the subdriver's resources */
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->sd_desc->stopN)
+ gspca_dev->sd_desc->stopN(gspca_dev);
+ destroy_urbs(gspca_dev);
+ gspca_input_destroy_urb(gspca_dev);
+ gspca_set_alt0(gspca_dev);
+ gspca_input_create_urb(gspca_dev);
if (gspca_dev->sd_desc->stop0)
gspca_dev->sd_desc->stop0(gspca_dev);
PDEBUG(D_STREAM, "stream off OK");
@@ -847,14 +847,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
struct ep_tb_s ep_tb[MAX_ALT];
int n, ret, xfer, alt, alt_idx;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
-
- if (!gspca_dev->present) {
- ret = -ENODEV;
- goto unlock;
- }
-
/* reset the streaming variables */
gspca_dev->image = NULL;
gspca_dev->image_len = 0;
@@ -869,7 +861,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
if (gspca_dev->sd_desc->isoc_init) {
ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
if (ret < 0)
- goto unlock;
+ return ret;
}
xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
: USB_ENDPOINT_XFER_ISOC;
@@ -880,8 +872,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
if (ep == NULL) {
pr_err("bad altsetting %d\n", gspca_dev->alt);
- ret = -EIO;
- goto out;
+ return -EIO;
}
ep_tb[0].alt = gspca_dev->alt;
alt_idx = 1;
@@ -892,8 +883,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
if (alt_idx <= 0) {
pr_err("no transfer endpoint found\n");
- ret = -EIO;
- goto unlock;
+ return -EIO;
}
}
@@ -988,8 +978,6 @@ retry:
}
out:
gspca_input_create_urb(gspca_dev);
-unlock:
- mutex_unlock(&gspca_dev->usb_lock);
return ret;
}
@@ -1006,6 +994,8 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev)
/* set the current control values to their default values
* which may have changed in sd_init() */
+ /* does nothing if ctrl_handler == NULL */
+ v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
ctrl = gspca_dev->cam.ctrls;
if (ctrl != NULL) {
for (i = 0;
@@ -1057,77 +1047,50 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev,
static int vidioc_g_register(struct file *file, void *priv,
struct v4l2_dbg_register *reg)
{
- int ret;
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
if (!gspca_dev->sd_desc->get_chip_ident)
- return -EINVAL;
+ return -ENOTTY;
if (!gspca_dev->sd_desc->get_register)
- return -EINVAL;
+ return -ENOTTY;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- ret = gspca_dev->sd_desc->get_register(gspca_dev, reg);
- else
- ret = -ENODEV;
- mutex_unlock(&gspca_dev->usb_lock);
-
- return ret;
+ return gspca_dev->sd_desc->get_register(gspca_dev, reg);
}
static int vidioc_s_register(struct file *file, void *priv,
struct v4l2_dbg_register *reg)
{
- int ret;
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
if (!gspca_dev->sd_desc->get_chip_ident)
- return -EINVAL;
+ return -ENOTTY;
if (!gspca_dev->sd_desc->set_register)
- return -EINVAL;
+ return -ENOTTY;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- ret = gspca_dev->sd_desc->set_register(gspca_dev, reg);
- else
- ret = -ENODEV;
- mutex_unlock(&gspca_dev->usb_lock);
-
- return ret;
+ return gspca_dev->sd_desc->set_register(gspca_dev, reg);
}
#endif
static int vidioc_g_chip_ident(struct file *file, void *priv,
struct v4l2_dbg_chip_ident *chip)
{
- int ret;
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
if (!gspca_dev->sd_desc->get_chip_ident)
- return -EINVAL;
+ return -ENOTTY;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
- else
- ret = -ENODEV;
- mutex_unlock(&gspca_dev->usb_lock);
-
- return ret;
+ return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
}
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *fmtdesc)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int i, j, index;
__u32 fmt_tb[8];
@@ -1169,7 +1132,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int mode;
mode = gspca_dev->curr_mode;
@@ -1214,7 +1177,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file,
void *priv,
struct v4l2_format *fmt)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int ret;
ret = try_fmt_vid_cap(gspca_dev, fmt);
@@ -1226,7 +1189,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file,
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *fmt)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int ret;
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
@@ -1265,7 +1228,7 @@ out:
static int vidioc_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int i;
__u32 index = 0;
@@ -1291,7 +1254,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
static int vidioc_enum_frameintervals(struct file *filp, void *priv,
struct v4l2_frmivalenum *fival)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(filp);
int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
__u32 i;
@@ -1316,31 +1279,30 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv,
return -EINVAL;
}
-static void gspca_release(struct video_device *vfd)
+static void gspca_release(struct v4l2_device *v4l2_device)
{
- struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
+ struct gspca_dev *gspca_dev =
+ container_of(v4l2_device, struct gspca_dev, v4l2_dev);
PDEBUG(D_PROBE, "%s released",
video_device_node_name(&gspca_dev->vdev));
+ v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
+ v4l2_device_unregister(&gspca_dev->v4l2_dev);
kfree(gspca_dev->usb_buf);
kfree(gspca_dev);
}
static int dev_open(struct file *file)
{
- struct gspca_dev *gspca_dev;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
PDEBUG(D_STREAM, "[%s] open", current->comm);
- gspca_dev = (struct gspca_dev *) video_devdata(file);
- if (!gspca_dev->present)
- return -ENODEV;
/* protect the subdriver against rmmod */
if (!try_module_get(gspca_dev->module))
return -ENODEV;
- file->private_data = gspca_dev;
#ifdef GSPCA_DEBUG
/* activate the v4l2 debug */
if (gspca_debug & D_V4L2)
@@ -1350,49 +1312,44 @@ static int dev_open(struct file *file)
gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL
| V4L2_DEBUG_IOCTL_ARG);
#endif
- return 0;
+ return v4l2_fh_open(file);
}
static int dev_close(struct file *file)
{
- struct gspca_dev *gspca_dev = file->private_data;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
PDEBUG(D_STREAM, "[%s] close", current->comm);
- if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+
+ /* Needed for gspca_stream_off, always lock before queue_lock! */
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock)) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ return -ERESTARTSYS;
+ }
+
/* if the file did the capture, free the streaming resources */
if (gspca_dev->capt_file == file) {
- if (gspca_dev->streaming) {
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->usb_err = 0;
+ if (gspca_dev->streaming)
gspca_stream_off(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- }
frame_free(gspca_dev);
}
- file->private_data = NULL;
module_put(gspca_dev->module);
mutex_unlock(&gspca_dev->queue_lock);
+ mutex_unlock(&gspca_dev->usb_lock);
PDEBUG(D_STREAM, "close done");
- return 0;
+ return v4l2_fh_release(file);
}
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct gspca_dev *gspca_dev = priv;
- int ret;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
- /* protect the access to the usb device */
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
- if (!gspca_dev->present) {
- ret = -ENODEV;
- goto out;
- }
strlcpy((char *) cap->driver, gspca_dev->sd_desc->name,
sizeof cap->driver);
if (gspca_dev->dev->product != NULL) {
@@ -1406,13 +1363,11 @@ static int vidioc_querycap(struct file *file, void *priv,
}
usb_make_path(gspca_dev->dev, (char *) cap->bus_info,
sizeof(cap->bus_info));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING
| V4L2_CAP_READWRITE;
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
}
static int get_ctrl(struct gspca_dev *gspca_dev,
@@ -1435,7 +1390,7 @@ static int get_ctrl(struct gspca_dev *gspca_dev,
static int vidioc_queryctrl(struct file *file, void *priv,
struct v4l2_queryctrl *q_ctrl)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
const struct ctrl *ctrls;
struct gspca_ctrl *gspca_ctrl;
int i, idx;
@@ -1478,10 +1433,10 @@ static int vidioc_queryctrl(struct file *file, void *priv,
static int vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
const struct ctrl *ctrls;
struct gspca_ctrl *gspca_ctrl;
- int idx, ret;
+ int idx;
idx = get_ctrl(gspca_dev, ctrl->id);
if (idx < 0)
@@ -1501,74 +1456,52 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
return -ERANGE;
}
PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
- if (!gspca_dev->present) {
- ret = -ENODEV;
- goto out;
- }
gspca_dev->usb_err = 0;
- if (ctrls->set != NULL) {
- ret = ctrls->set(gspca_dev, ctrl->value);
- goto out;
- }
+ if (ctrls->set != NULL)
+ return ctrls->set(gspca_dev, ctrl->value);
if (gspca_ctrl != NULL) {
gspca_ctrl->val = ctrl->value;
if (ctrls->set_control != NULL
&& gspca_dev->streaming)
ctrls->set_control(gspca_dev);
}
- ret = gspca_dev->usb_err;
-out:
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ return gspca_dev->usb_err;
}
static int vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
const struct ctrl *ctrls;
- int idx, ret;
+ int idx;
idx = get_ctrl(gspca_dev, ctrl->id);
if (idx < 0)
return -EINVAL;
ctrls = &gspca_dev->sd_desc->ctrls[idx];
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
- if (!gspca_dev->present) {
- ret = -ENODEV;
- goto out;
- }
gspca_dev->usb_err = 0;
- if (ctrls->get != NULL) {
- ret = ctrls->get(gspca_dev, &ctrl->value);
- goto out;
- }
+ if (ctrls->get != NULL)
+ return ctrls->get(gspca_dev, &ctrl->value);
if (gspca_dev->cam.ctrls != NULL)
ctrl->value = gspca_dev->cam.ctrls[idx].val;
- ret = 0;
-out:
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ return 0;
}
static int vidioc_querymenu(struct file *file, void *priv,
struct v4l2_querymenu *qmenu)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
if (!gspca_dev->sd_desc->querymenu)
- return -EINVAL;
+ return -ENOTTY;
return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu);
}
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
if (input->index != 0)
return -EINVAL;
@@ -1595,7 +1528,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *rb)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int i, ret = 0, streaming;
i = rb->memory; /* (avoid compilation warning) */
@@ -1635,10 +1568,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
/* stop streaming */
streaming = gspca_dev->streaming;
if (streaming) {
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->usb_err = 0;
gspca_stream_off(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
/* Don't restart the stream when switching from read
* to mmap mode */
@@ -1666,7 +1596,7 @@ out:
static int vidioc_querybuf(struct file *file, void *priv,
struct v4l2_buffer *v4l2_buf)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
if (v4l2_buf->index < 0
@@ -1681,7 +1611,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
static int vidioc_streamon(struct file *file, void *priv,
enum v4l2_buf_type buf_type)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int ret;
if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1722,7 +1652,7 @@ out:
static int vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type buf_type)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
int ret;
if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1743,13 +1673,7 @@ static int vidioc_streamoff(struct file *file, void *priv,
}
/* stop streaming */
- if (mutex_lock_interruptible(&gspca_dev->usb_lock)) {
- ret = -ERESTARTSYS;
- goto out;
- }
- gspca_dev->usb_err = 0;
gspca_stream_off(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
/* In case another thread is waiting in dqbuf */
wake_up_interruptible(&gspca_dev->wq);
@@ -1766,71 +1690,44 @@ out:
static int vidioc_g_jpegcomp(struct file *file, void *priv,
struct v4l2_jpegcompression *jpegcomp)
{
- struct gspca_dev *gspca_dev = priv;
- int ret;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
if (!gspca_dev->sd_desc->get_jcomp)
- return -EINVAL;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
+ return -ENOTTY;
gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
- else
- ret = -ENODEV;
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
}
static int vidioc_s_jpegcomp(struct file *file, void *priv,
struct v4l2_jpegcompression *jpegcomp)
{
- struct gspca_dev *gspca_dev = priv;
- int ret;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
if (!gspca_dev->sd_desc->set_jcomp)
- return -EINVAL;
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
+ return -ENOTTY;
gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
- else
- ret = -ENODEV;
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
}
static int vidioc_g_parm(struct file *filp, void *priv,
struct v4l2_streamparm *parm)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(filp);
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;
- if (gspca_dev->present) {
- gspca_dev->usb_err = 0;
- gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
- ret = gspca_dev->usb_err;
- } else {
- ret = -ENODEV;
- }
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
+ return gspca_dev->usb_err;
}
-
return 0;
}
static int vidioc_s_parm(struct file *filp, void *priv,
struct v4l2_streamparm *parm)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(filp);
int n;
n = parm->parm.capture.readbuffers;
@@ -1840,19 +1737,9 @@ static int vidioc_s_parm(struct file *filp, void *priv,
gspca_dev->nbufread = n;
if (gspca_dev->sd_desc->set_streamparm) {
- int ret;
-
- if (mutex_lock_interruptible(&gspca_dev->usb_lock))
- return -ERESTARTSYS;
- if (gspca_dev->present) {
- gspca_dev->usb_err = 0;
- gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
- ret = gspca_dev->usb_err;
- } else {
- ret = -ENODEV;
- }
- mutex_unlock(&gspca_dev->usb_lock);
- return ret;
+ gspca_dev->usb_err = 0;
+ gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
+ return gspca_dev->usb_err;
}
return 0;
@@ -1860,7 +1747,7 @@ static int vidioc_s_parm(struct file *filp, void *priv,
static int dev_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct gspca_dev *gspca_dev = file->private_data;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
struct page *page;
unsigned long addr, start, size;
@@ -1872,10 +1759,6 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma)
if (mutex_lock_interruptible(&gspca_dev->queue_lock))
return -ERESTARTSYS;
- if (!gspca_dev->present) {
- ret = -ENODEV;
- goto out;
- }
if (gspca_dev->capt_file != file) {
ret = -EINVAL;
goto out;
@@ -1963,7 +1846,7 @@ static int frame_ready(struct gspca_dev *gspca_dev, struct file *file,
static int vidioc_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *v4l2_buf)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
int i, j, ret;
@@ -2003,14 +1886,6 @@ static int vidioc_dqbuf(struct file *file, void *priv,
gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES;
- if (gspca_dev->sd_desc->dq_callback) {
- mutex_lock(&gspca_dev->usb_lock);
- gspca_dev->usb_err = 0;
- if (gspca_dev->present)
- gspca_dev->sd_desc->dq_callback(gspca_dev);
- mutex_unlock(&gspca_dev->usb_lock);
- }
-
frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE;
memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf);
PDEBUG(D_FRAM, "dqbuf %d", j);
@@ -2027,6 +1902,15 @@ static int vidioc_dqbuf(struct file *file, void *priv,
}
out:
mutex_unlock(&gspca_dev->queue_lock);
+
+ if (ret == 0 && gspca_dev->sd_desc->dq_callback) {
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ gspca_dev->sd_desc->dq_callback(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+
return ret;
}
@@ -2039,7 +1923,7 @@ out:
static int vidioc_qbuf(struct file *file, void *priv,
struct v4l2_buffer *v4l2_buf)
{
- struct gspca_dev *gspca_dev = priv;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
int i, index, ret;
@@ -2098,6 +1982,10 @@ static int read_alloc(struct gspca_dev *gspca_dev,
int i, ret;
PDEBUG(D_STREAM, "read alloc");
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+
if (gspca_dev->nframes == 0) {
struct v4l2_requestbuffers rb;
@@ -2108,7 +1996,7 @@ static int read_alloc(struct gspca_dev *gspca_dev,
ret = vidioc_reqbufs(file, gspca_dev, &rb);
if (ret != 0) {
PDEBUG(D_STREAM, "read reqbuf err %d", ret);
- return ret;
+ goto out;
}
memset(&v4l2_buf, 0, sizeof v4l2_buf);
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
@@ -2118,61 +2006,69 @@ static int read_alloc(struct gspca_dev *gspca_dev,
ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf);
if (ret != 0) {
PDEBUG(D_STREAM, "read qbuf err: %d", ret);
- return ret;
+ goto out;
}
}
- gspca_dev->memory = GSPCA_MEMORY_READ;
}
/* start streaming */
ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE);
if (ret != 0)
PDEBUG(D_STREAM, "read streamon err %d", ret);
+out:
+ mutex_unlock(&gspca_dev->usb_lock);
return ret;
}
static unsigned int dev_poll(struct file *file, poll_table *wait)
{
- struct gspca_dev *gspca_dev = file->private_data;
- int ret;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
+ unsigned long req_events = poll_requested_events(wait);
+ int ret = 0;
PDEBUG(D_FRAM, "poll");
- poll_wait(file, &gspca_dev->wq, wait);
+ if (req_events & POLLPRI)
+ ret |= v4l2_ctrl_poll(file, wait);
- /* if reqbufs is not done, the user would use read() */
- if (gspca_dev->memory == GSPCA_MEMORY_NO) {
- ret = read_alloc(gspca_dev, file);
- if (ret != 0)
- return POLLERR;
- }
+ if (req_events & (POLLIN | POLLRDNORM)) {
+ /* if reqbufs is not done, the user would use read() */
+ if (gspca_dev->memory == GSPCA_MEMORY_NO) {
+ if (read_alloc(gspca_dev, file) != 0) {
+ ret |= POLLERR;
+ goto out;
+ }
+ }
- if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0)
- return POLLERR;
+ poll_wait(file, &gspca_dev->wq, wait);
- /* check if an image has been received */
- if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i))
- ret = POLLIN | POLLRDNORM; /* yes */
- else
- ret = 0;
- mutex_unlock(&gspca_dev->queue_lock);
+ /* check if an image has been received */
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) {
+ ret |= POLLERR;
+ goto out;
+ }
+ if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i))
+ ret |= POLLIN | POLLRDNORM;
+ mutex_unlock(&gspca_dev->queue_lock);
+ }
+
+out:
if (!gspca_dev->present)
- return POLLHUP;
+ ret |= POLLHUP;
+
return ret;
}
static ssize_t dev_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
- struct gspca_dev *gspca_dev = file->private_data;
+ struct gspca_dev *gspca_dev = video_drvdata(file);
struct gspca_frame *frame;
struct v4l2_buffer v4l2_buf;
struct timeval timestamp;
int n, ret, ret2;
PDEBUG(D_FRAM, "read (%zd)", count);
- if (!gspca_dev->present)
- return -ENODEV;
if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */
ret = read_alloc(gspca_dev, file);
if (ret != 0)
@@ -2266,13 +2162,15 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
.vidioc_s_register = vidioc_s_register,
#endif
.vidioc_g_chip_ident = vidioc_g_chip_ident,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static const struct video_device gspca_template = {
.name = "gspca main driver",
.fops = &dev_fops,
.ioctl_ops = &dev_ioctl_ops,
- .release = gspca_release,
+ .release = video_device_release_empty, /* We use v4l2_dev.release */
};
/* initialize the controls */
@@ -2344,9 +2242,24 @@ int gspca_dev_probe2(struct usb_interface *intf,
}
}
+ gspca_dev->v4l2_dev.release = gspca_release;
+ ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev);
+ if (ret)
+ goto out;
gspca_dev->sd_desc = sd_desc;
gspca_dev->nbufread = 2;
gspca_dev->empty_packet = -1; /* don't check the empty packets */
+ gspca_dev->vdev = gspca_template;
+ gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev;
+ video_set_drvdata(&gspca_dev->vdev, gspca_dev);
+ set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags);
+ gspca_dev->module = module;
+ gspca_dev->present = 1;
+
+ mutex_init(&gspca_dev->usb_lock);
+ gspca_dev->vdev.lock = &gspca_dev->usb_lock;
+ mutex_init(&gspca_dev->queue_lock);
+ init_waitqueue_head(&gspca_dev->wq);
/* configure the subdriver and initialize the USB device */
ret = sd_desc->config(gspca_dev, id);
@@ -2357,21 +2270,26 @@ int gspca_dev_probe2(struct usb_interface *intf,
ret = sd_desc->init(gspca_dev);
if (ret < 0)
goto out;
+ if (sd_desc->init_controls)
+ ret = sd_desc->init_controls(gspca_dev);
+ if (ret < 0)
+ goto out;
gspca_set_default_mode(gspca_dev);
ret = gspca_input_connect(gspca_dev);
if (ret)
goto out;
- mutex_init(&gspca_dev->usb_lock);
- mutex_init(&gspca_dev->queue_lock);
- init_waitqueue_head(&gspca_dev->wq);
+ /*
+ * Don't take usb_lock for these ioctls. This improves latency if
+ * usb_lock is taken for a long time, e.g. when changing a control
+ * value, and a new frame is ready to be dequeued.
+ */
+ v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF);
+ v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF);
+ v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF);
/* init video stuff */
- memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template);
- gspca_dev->vdev.parent = &intf->dev;
- gspca_dev->module = module;
- gspca_dev->present = 1;
ret = video_register_device(&gspca_dev->vdev,
VFL_TYPE_GRABBER,
-1);
@@ -2391,6 +2309,7 @@ out:
if (gspca_dev->input_dev)
input_unregister_device(gspca_dev->input_dev);
#endif
+ v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler);
kfree(gspca_dev->usb_buf);
kfree(gspca_dev);
return ret;
@@ -2437,11 +2356,12 @@ void gspca_disconnect(struct usb_interface *intf)
PDEBUG(D_PROBE, "%s disconnect",
video_device_node_name(&gspca_dev->vdev));
+
mutex_lock(&gspca_dev->usb_lock);
+ usb_set_intfdata(intf, NULL);
+ gspca_dev->dev = NULL;
gspca_dev->present = 0;
- wake_up_interruptible(&gspca_dev->wq);
-
destroy_urbs(gspca_dev);
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -2452,18 +2372,19 @@ void gspca_disconnect(struct usb_interface *intf)
input_unregister_device(input_dev);
}
#endif
+ /* Free subdriver's streaming resources / stop sd workqueue(s) */
+ if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ gspca_dev->streaming = 0;
+ wake_up_interruptible(&gspca_dev->wq);
- /* the device is freed at exit of this function */
- gspca_dev->dev = NULL;
- mutex_unlock(&gspca_dev->usb_lock);
+ v4l2_device_disconnect(&gspca_dev->v4l2_dev);
+ video_unregister_device(&gspca_dev->vdev);
- usb_set_intfdata(intf, NULL);
+ mutex_unlock(&gspca_dev->usb_lock);
- /* release the device */
/* (this will call gspca_release() immediately or on last close) */
- video_unregister_device(&gspca_dev->vdev);
-
-/* PDEBUG(D_PROBE, "disconnect complete"); */
+ v4l2_device_put(&gspca_dev->v4l2_dev);
}
EXPORT_SYMBOL(gspca_disconnect);
@@ -2474,7 +2395,9 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message)
if (!gspca_dev->streaming)
return 0;
+ mutex_lock(&gspca_dev->usb_lock);
gspca_dev->frozen = 1; /* avoid urb error messages */
+ gspca_dev->usb_err = 0;
if (gspca_dev->sd_desc->stopN)
gspca_dev->sd_desc->stopN(gspca_dev);
destroy_urbs(gspca_dev);
@@ -2482,6 +2405,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message)
gspca_set_alt0(gspca_dev);
if (gspca_dev->sd_desc->stop0)
gspca_dev->sd_desc->stop0(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
return 0;
}
EXPORT_SYMBOL(gspca_suspend);
@@ -2489,105 +2413,28 @@ EXPORT_SYMBOL(gspca_suspend);
int gspca_resume(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ int streaming, ret = 0;
+ mutex_lock(&gspca_dev->usb_lock);
gspca_dev->frozen = 0;
+ gspca_dev->usb_err = 0;
gspca_dev->sd_desc->init(gspca_dev);
gspca_input_create_urb(gspca_dev);
- if (gspca_dev->streaming)
- return gspca_init_transfer(gspca_dev);
- return 0;
+ /*
+ * Most subdrivers send all ctrl values on sd_start and thus
+ * only write to the device registers on s_ctrl when streaming ->
+ * Clear streaming to avoid setting all ctrls twice.
+ */
+ streaming = gspca_dev->streaming;
+ gspca_dev->streaming = 0;
+ v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
+ if (streaming)
+ ret = gspca_init_transfer(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
}
EXPORT_SYMBOL(gspca_resume);
#endif
-/* -- cam driver utility functions -- */
-
-/* auto gain and exposure algorithm based on the knee algorithm described here:
- http://ytse.tricolour.net/docs/LowLightOptimization.html
-
- Returns 0 if no changes were made, 1 if the gain and or exposure settings
- where changed. */
-int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
- int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee)
-{
- int i, steps, gain, orig_gain, exposure, orig_exposure, autogain;
- const struct ctrl *gain_ctrl = NULL;
- const struct ctrl *exposure_ctrl = NULL;
- const struct ctrl *autogain_ctrl = NULL;
- int retval = 0;
-
- for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) {
- if (gspca_dev->ctrl_dis & (1 << i))
- continue;
- if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN)
- gain_ctrl = &gspca_dev->sd_desc->ctrls[i];
- if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE)
- exposure_ctrl = &gspca_dev->sd_desc->ctrls[i];
- if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN)
- autogain_ctrl = &gspca_dev->sd_desc->ctrls[i];
- }
- if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) {
- PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called "
- "on cam without (auto)gain/exposure");
- return 0;
- }
-
- if (gain_ctrl->get(gspca_dev, &gain) ||
- exposure_ctrl->get(gspca_dev, &exposure) ||
- autogain_ctrl->get(gspca_dev, &autogain) || !autogain)
- return 0;
-
- orig_gain = gain;
- orig_exposure = exposure;
-
- /* If we are of a multiple of deadzone, do multiple steps to reach the
- 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",
- avg_lum, desired_avg_lum, steps);
-
- for (i = 0; i < steps; i++) {
- if (avg_lum > desired_avg_lum) {
- if (gain > gain_knee)
- gain--;
- else if (exposure > exposure_knee)
- exposure--;
- else if (gain > gain_ctrl->qctrl.default_value)
- gain--;
- else if (exposure > exposure_ctrl->qctrl.minimum)
- exposure--;
- else if (gain > gain_ctrl->qctrl.minimum)
- gain--;
- else
- break;
- } else {
- if (gain < gain_ctrl->qctrl.default_value)
- gain++;
- else if (exposure < exposure_knee)
- exposure++;
- else if (gain < gain_knee)
- gain++;
- else if (exposure < exposure_ctrl->qctrl.maximum)
- exposure++;
- else if (gain < gain_ctrl->qctrl.maximum)
- gain++;
- else
- break;
- }
- }
-
- if (gain != orig_gain) {
- gain_ctrl->set(gspca_dev, gain);
- retval = 1;
- }
- if (exposure != orig_exposure) {
- exposure_ctrl->set(gspca_dev, exposure);
- retval = 1;
- }
-
- return retval;
-}
-EXPORT_SYMBOL(gspca_auto_gain_n_exposure);
/* -- module insert / remove -- */
static int __init gspca_init(void)
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
index 589009f4496..dc688c7f5e4 100644
--- a/drivers/media/video/gspca/gspca.h
+++ b/drivers/media/video/gspca/gspca.h
@@ -6,6 +6,8 @@
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
#include <linux/mutex.h>
/* compilation option */
@@ -115,6 +117,7 @@ struct sd_desc {
/* mandatory operations */
cam_cf_op config; /* called on probe */
cam_op init; /* called on probe and resume */
+ cam_op init_controls; /* called on probe */
cam_op start; /* called on stream on after URBs creation */
cam_pkt_op pkt_scan;
/* optional operations */
@@ -158,8 +161,10 @@ struct gspca_frame {
struct gspca_dev {
struct video_device vdev; /* !! must be the first item */
struct module *module; /* subdriver handling the device */
+ struct v4l2_device v4l2_dev;
struct usb_device *dev;
struct file *capt_file; /* file doing video capture */
+ /* protected by queue_lock */
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
struct input_dev *input_dev;
char phys[64]; /* physical device path */
@@ -169,6 +174,16 @@ struct gspca_dev {
const struct sd_desc *sd_desc; /* subdriver description */
unsigned ctrl_dis; /* disabled controls (bit map) */
unsigned ctrl_inac; /* inactive controls (bit map) */
+ struct v4l2_ctrl_handler ctrl_handler;
+
+ /* autogain and exposure or gain control cluster, these are global as
+ the autogain/exposure functions in autogain_functions.c use them */
+ struct {
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ int exp_too_low_cnt, exp_too_high_cnt;
+ };
#define USB_BUF_SZ 64
__u8 *usb_buf; /* buffer for USB exchanges */
@@ -189,7 +204,7 @@ struct gspca_dev {
u8 fr_o; /* next frame to dequeue */
__u8 last_packet_type;
__s8 empty_packet; /* if (-1) don't check empty packets */
- __u8 streaming;
+ __u8 streaming; /* protected by both mutexes (*) */
__u8 curr_mode; /* current camera mode */
__u32 pixfmt; /* current mode parameters */
@@ -211,6 +226,10 @@ struct gspca_dev {
__u8 iface; /* USB interface number */
__u8 alt; /* USB alternate setting */
u8 audio; /* presence of audio device */
+
+ /* (*) These variables are proteced by both usb_lock and queue_lock,
+ that is any code setting them is holding *both*, which means that
+ any code getting them needs to hold at least one of them */
};
int gspca_dev_probe(struct usb_interface *intf,
@@ -232,6 +251,9 @@ void gspca_frame_add(struct gspca_dev *gspca_dev,
int gspca_suspend(struct usb_interface *intf, pm_message_t message);
int gspca_resume(struct usb_interface *intf);
#endif
-int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum,
+int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum,
int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee);
+int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev,
+ int avg_lum, int desired_avg_lum, int deadzone);
+
#endif /* GSPCAV2_H */
diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c
index 53f58ef367c..9c591c7c6f5 100644
--- a/drivers/media/video/gspca/jl2005bcd.c
+++ b/drivers/media/video/gspca/jl2005bcd.c
@@ -335,7 +335,11 @@ static void jl2005c_dostream(struct work_struct *work)
goto quit_stream;
}
- while (gspca_dev->present && gspca_dev->streaming) {
+ while (gspca_dev->dev && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
/* Check if this is a new frame. If so, start the frame first */
if (!header_read) {
mutex_lock(&gspca_dev->usb_lock);
@@ -367,7 +371,7 @@ static void jl2005c_dostream(struct work_struct *work)
buffer, act_len);
header_read = 1;
}
- while (bytes_left > 0 && gspca_dev->present) {
+ while (bytes_left > 0 && gspca_dev->dev) {
data_len = bytes_left > JL2005C_MAX_TRANSFER ?
JL2005C_MAX_TRANSFER : bytes_left;
ret = usb_bulk_msg(gspca_dev->dev,
@@ -390,7 +394,7 @@ static void jl2005c_dostream(struct work_struct *work)
}
}
quit_stream:
- if (gspca_dev->present) {
+ if (gspca_dev->dev) {
mutex_lock(&gspca_dev->usb_lock);
jl2005c_stop(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
index b0231465afa..ec7b21ee79f 100644
--- a/drivers/media/video/gspca/mars.c
+++ b/drivers/media/video/gspca/mars.c
@@ -30,22 +30,19 @@ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver");
MODULE_LICENSE("GPL");
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- COLORS,
- GAMMA,
- SHARPNESS,
- ILLUM_TOP,
- ILLUM_BOT,
- NCTRLS /* number of controls */
-};
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *sharpness;
+ struct v4l2_ctrl *gamma;
+ struct { /* illuminator control cluster */
+ struct v4l2_ctrl *illum_top;
+ struct v4l2_ctrl *illum_bottom;
+ };
+ struct v4l2_ctrl *jpegqual;
u8 quality;
#define QUALITY_MIN 40
@@ -56,89 +53,10 @@ struct sd {
};
/* V4L2 controls supported by the driver */
-static void setbrightness(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
-static void setgamma(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 30,
- .step = 1,
- .default_value = 15,
- },
- .set_control = setbrightness
- },
-[COLORS] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Color",
- .minimum = 1,
- .maximum = 255,
- .step = 1,
- .default_value = 200,
- },
- .set_control = setcolors
- },
-[GAMMA] = {
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setgamma
- },
-[SHARPNESS] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 2,
- .step = 1,
- .default_value = 1,
- },
- .set_control = setsharpness
- },
-[ILLUM_TOP] = {
- {
- .id = V4L2_CID_ILLUMINATORS_1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Top illuminator",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- .flags = V4L2_CTRL_FLAG_UPDATE,
- },
- .set = sd_setilluminator1
- },
-[ILLUM_BOT] = {
- {
- .id = V4L2_CID_ILLUMINATORS_2,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Bottom illuminator",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- .flags = V4L2_CTRL_FLAG_UPDATE,
- },
- .set = sd_setilluminator2
- },
-};
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val);
+static void setcolors(struct gspca_dev *gspca_dev, s32 val);
+static void setgamma(struct gspca_dev *gspca_dev, s32 val);
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val);
static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
@@ -198,59 +116,130 @@ static void mi_w(struct gspca_dev *gspca_dev,
reg_w(gspca_dev, 4);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
+static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
gspca_dev->usb_buf[0] = 0x61;
- gspca_dev->usb_buf[1] = sd->ctrls[BRIGHTNESS].val;
+ gspca_dev->usb_buf[1] = val;
reg_w(gspca_dev, 2);
}
-static void setcolors(struct gspca_dev *gspca_dev)
+static void setcolors(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- s16 val;
-
- val = sd->ctrls[COLORS].val;
gspca_dev->usb_buf[0] = 0x5f;
gspca_dev->usb_buf[1] = val << 3;
gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04;
reg_w(gspca_dev, 3);
}
-static void setgamma(struct gspca_dev *gspca_dev)
+static void setgamma(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
gspca_dev->usb_buf[0] = 0x06;
- gspca_dev->usb_buf[1] = sd->ctrls[GAMMA].val * 0x40;
+ gspca_dev->usb_buf[1] = val * 0x40;
reg_w(gspca_dev, 2);
}
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
gspca_dev->usb_buf[0] = 0x67;
- gspca_dev->usb_buf[1] = sd->ctrls[SHARPNESS].val * 4 + 3;
+ gspca_dev->usb_buf[1] = val * 4 + 3;
reg_w(gspca_dev, 2);
}
-static void setilluminators(struct gspca_dev *gspca_dev)
+static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
+ /* both are off if not streaming */
gspca_dev->usb_buf[0] = 0x22;
- if (sd->ctrls[ILLUM_TOP].val)
+ if (top)
gspca_dev->usb_buf[1] = 0x76;
- else if (sd->ctrls[ILLUM_BOT].val)
+ else if (bottom)
gspca_dev->usb_buf[1] = 0x7a;
else
gspca_dev->usb_buf[1] = 0x7e;
reg_w(gspca_dev, 2);
}
+static int mars_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (ctrl->id == V4L2_CID_ILLUMINATORS_1) {
+ /* only one can be on at a time */
+ if (ctrl->is_new && ctrl->val)
+ sd->illum_bottom->val = 0;
+ if (sd->illum_bottom->is_new && sd->illum_bottom->val)
+ sd->illum_top->val = 0;
+ }
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setbrightness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ setcolors(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAMMA:
+ setgamma(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_ILLUMINATORS_1:
+ setilluminators(gspca_dev, sd->illum_top->val,
+ sd->illum_bottom->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ jpeg_set_qual(sd->jpeg_hdr, ctrl->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops mars_ctrl_ops = {
+ .s_ctrl = mars_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 7);
+ sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 30, 1, 15);
+ sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 200);
+ sd->gamma = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 3, 1, 1);
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 2, 1, 1);
+ sd->illum_top = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);
+ sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE;
+ sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+ V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
+ sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE;
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_cluster(2, &sd->illum_top);
+ return 0;
+}
+
/* this function is called at probe time */
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
@@ -261,7 +250,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
- cam->ctrls = sd->ctrls;
sd->quality = QUALITY_DEF;
return 0;
}
@@ -269,7 +257,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT);
return 0;
}
@@ -282,7 +269,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
data = gspca_dev->usb_buf;
@@ -301,7 +288,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
data[5] = 0x30; /* reg 4, MI, PAS5101 :
* 0x30 for 24mhz , 0x28 for 12mhz */
data[6] = 0x02; /* reg 5, H start - was 0x04 */
- data[7] = sd->ctrls[GAMMA].val * 0x40; /* reg 0x06: gamma */
+ data[7] = v4l2_ctrl_g_ctrl(sd->gamma) * 0x40; /* reg 0x06: gamma */
data[8] = 0x01; /* reg 7, V start - was 0x03 */
/* if (h_size == 320 ) */
/* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */
@@ -333,16 +320,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* reg 0x5f/0x60 (LE) = saturation */
/* h (60): xxxx x100
* l (5f): xxxx x000 */
- data[2] = sd->ctrls[COLORS].val << 3;
- data[3] = ((sd->ctrls[COLORS].val >> 2) & 0xf8) | 0x04;
- data[4] = sd->ctrls[BRIGHTNESS].val; /* reg 0x61 = brightness */
+ data[2] = v4l2_ctrl_g_ctrl(sd->saturation) << 3;
+ data[3] = ((v4l2_ctrl_g_ctrl(sd->saturation) >> 2) & 0xf8) | 0x04;
+ data[4] = v4l2_ctrl_g_ctrl(sd->brightness); /* reg 0x61 = brightness */
data[5] = 0x00;
reg_w(gspca_dev, 6);
data[0] = 0x67;
/*jfm: from win trace*/
- data[1] = sd->ctrls[SHARPNESS].val * 4 + 3;
+ data[1] = v4l2_ctrl_g_ctrl(sd->sharpness) * 4 + 3;
data[2] = 0x14;
reg_w(gspca_dev, 3);
@@ -365,7 +352,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
data[1] = 0x4d; /* ISOC transferring enable... */
reg_w(gspca_dev, 2);
- gspca_dev->ctrl_inac = 0; /* activate the illuminator controls */
+ setilluminators(gspca_dev, v4l2_ctrl_g_ctrl(sd->illum_top),
+ v4l2_ctrl_g_ctrl(sd->illum_bottom));
+
return gspca_dev->usb_err;
}
@@ -373,11 +362,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT);
- if (sd->ctrls[ILLUM_TOP].val || sd->ctrls[ILLUM_BOT].val) {
- sd->ctrls[ILLUM_TOP].val = 0;
- sd->ctrls[ILLUM_BOT].val = 0;
- setilluminators(gspca_dev);
+ if (v4l2_ctrl_g_ctrl(sd->illum_top) ||
+ v4l2_ctrl_g_ctrl(sd->illum_bottom)) {
+ setilluminators(gspca_dev, false, false);
msleep(20);
}
@@ -424,43 +411,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* only one illuminator may be on */
- sd->ctrls[ILLUM_TOP].val = val;
- if (val)
- sd->ctrls[ILLUM_BOT].val = 0;
- setilluminators(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- /* only one illuminator may be on */
- sd->ctrls[ILLUM_BOT].val = val;
- if (val)
- sd->ctrls[ILLUM_TOP].val = 0;
- setilluminators(gspca_dev);
- return gspca_dev->usb_err;
-}
-
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+ if (ret)
+ return ret;
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
return 0;
}
@@ -470,7 +430,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
return 0;
@@ -479,10 +439,9 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = NCTRLS,
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.pkt_scan = sd_pkt_scan,
@@ -513,6 +472,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c
index 7167cac7359..42e021931e6 100644
--- a/drivers/media/video/gspca/nw80x.c
+++ b/drivers/media/video/gspca/nw80x.c
@@ -2001,6 +2001,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
return gspca_dev->usb_err;
}
+#define WANT_REGULAR_AUTOGAIN
+#define WANT_COARSE_EXPO_AUTOGAIN
#include "autogain_functions.h"
static void do_autogain(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
index 739e8a2a2d3..183457c5cfd 100644
--- a/drivers/media/video/gspca/ov519.c
+++ b/drivers/media/video/gspca/ov519.c
@@ -2804,7 +2804,7 @@ static void ov7xx0_configure(struct sd *sd)
/* add OV7670 here
* it appears to be wrongly detected as a 7610 by default */
if (rc < 0) {
- PDEBUG(D_ERR, "Error detecting sensor type");
+ pr_err("Error detecting sensor type\n");
return;
}
if ((rc & 3) == 3) {
@@ -2832,12 +2832,12 @@ static void ov7xx0_configure(struct sd *sd)
/* try to read product id registers */
high = i2c_r(sd, 0x0a);
if (high < 0) {
- PDEBUG(D_ERR, "Error detecting camera chip PID");
+ pr_err("Error detecting camera chip PID\n");
return;
}
low = i2c_r(sd, 0x0b);
if (low < 0) {
- PDEBUG(D_ERR, "Error detecting camera chip VER");
+ pr_err("Error detecting camera chip VER\n");
return;
}
if (high == 0x76) {
@@ -2863,7 +2863,7 @@ static void ov7xx0_configure(struct sd *sd)
sd->sensor = SEN_OV7660;
break;
default:
- PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low);
+ pr_err("Unknown sensor: 0x76%02x\n", low);
return;
}
} else {
@@ -2884,7 +2884,7 @@ static void ov6xx0_configure(struct sd *sd)
/* Detect sensor (sub)type */
rc = i2c_r(sd, OV7610_REG_COM_I);
if (rc < 0) {
- PDEBUG(D_ERR, "Error detecting sensor type");
+ pr_err("Error detecting sensor type\n");
return;
}
diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
index 04753391de3..b5acb1e4b4e 100644
--- a/drivers/media/video/gspca/ov534.c
+++ b/drivers/media/video/gspca/ov534.c
@@ -34,6 +34,8 @@
#include "gspca.h"
+#include <linux/fixp-arith.h>
+
#define OV534_REG_ADDRESS 0xf1 /* sensor address */
#define OV534_REG_SUBADDR 0xf2
#define OV534_REG_WRITE 0xf3
@@ -53,6 +55,8 @@ MODULE_LICENSE("GPL");
/* controls */
enum e_ctrl {
+ HUE,
+ SATURATION,
BRIGHTNESS,
CONTRAST,
GAIN,
@@ -63,7 +67,6 @@ enum e_ctrl {
SHARPNESS,
HFLIP,
VFLIP,
- COLORS,
LIGHTFREQ,
NCTRLS /* number of controls */
};
@@ -87,6 +90,8 @@ enum sensors {
};
/* V4L2 controls supported by the driver */
+static void sethue(struct gspca_dev *gspca_dev);
+static void setsaturation(struct gspca_dev *gspca_dev);
static void setbrightness(struct gspca_dev *gspca_dev);
static void setcontrast(struct gspca_dev *gspca_dev);
static void setgain(struct gspca_dev *gspca_dev);
@@ -96,13 +101,36 @@ static void setawb(struct gspca_dev *gspca_dev);
static void setaec(struct gspca_dev *gspca_dev);
static void setsharpness(struct gspca_dev *gspca_dev);
static void sethvflip(struct gspca_dev *gspca_dev);
-static void setcolors(struct gspca_dev *gspca_dev);
static void setlightfreq(struct gspca_dev *gspca_dev);
static int sd_start(struct gspca_dev *gspca_dev);
static void sd_stopN(struct gspca_dev *gspca_dev);
static const struct ctrl sd_ctrls[] = {
+[HUE] = {
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = -90,
+ .maximum = 90,
+ .step = 1,
+ .default_value = 0,
+ },
+ .set_control = sethue
+ },
+[SATURATION] = {
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = 64,
+ },
+ .set_control = setsaturation
+ },
[BRIGHTNESS] = {
{
.id = V4L2_CID_BRIGHTNESS,
@@ -223,18 +251,6 @@ static const struct ctrl sd_ctrls[] = {
},
.set_control = sethvflip
},
-[COLORS] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 6,
- .step = 1,
- .default_value = 3,
- },
- .set_control = setcolors
- },
[LIGHTFREQ] = {
{
.id = V4L2_CID_POWER_LINE_FREQUENCY,
@@ -684,7 +700,7 @@ static const u8 sensor_init_772x[][2] = {
{ 0x9c, 0x20 },
{ 0x9e, 0x81 },
- { 0xa6, 0x04 },
+ { 0xa6, 0x07 },
{ 0x7e, 0x0c },
{ 0x7f, 0x16 },
{ 0x80, 0x2a },
@@ -955,6 +971,74 @@ static void set_frame_rate(struct gspca_dev *gspca_dev)
PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
}
+static void sethue(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int val;
+
+ val = sd->ctrls[HUE].val;
+ if (sd->sensor == SENSOR_OV767x) {
+ /* TBD */
+ } else {
+ s16 huesin;
+ s16 huecos;
+
+ /* fixp_sin and fixp_cos accept only positive values, while
+ * our val is between -90 and 90
+ */
+ val += 360;
+
+ /* According to the datasheet the registers expect HUESIN and
+ * HUECOS to be the result of the trigonometric functions,
+ * scaled by 0x80.
+ *
+ * The 0x100 here represents the maximun absolute value
+ * returned byt fixp_sin and fixp_cos, so the scaling will
+ * consider the result like in the interval [-1.0, 1.0].
+ */
+ huesin = fixp_sin(val) * 0x80 / 0x100;
+ huecos = fixp_cos(val) * 0x80 / 0x100;
+
+ if (huesin < 0) {
+ sccb_reg_write(gspca_dev, 0xab,
+ sccb_reg_read(gspca_dev, 0xab) | 0x2);
+ huesin = -huesin;
+ } else {
+ sccb_reg_write(gspca_dev, 0xab,
+ sccb_reg_read(gspca_dev, 0xab) & ~0x2);
+
+ }
+ sccb_reg_write(gspca_dev, 0xa9, (u8)huecos);
+ sccb_reg_write(gspca_dev, 0xaa, (u8)huesin);
+ }
+}
+
+static void setsaturation(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int val;
+
+ val = sd->ctrls[SATURATION].val;
+ if (sd->sensor == SENSOR_OV767x) {
+ int i;
+ static u8 color_tb[][6] = {
+ {0x42, 0x42, 0x00, 0x11, 0x30, 0x41},
+ {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52},
+ {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66},
+ {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80},
+ {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a},
+ {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8},
+ {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++)
+ sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]);
+ } else {
+ sccb_reg_write(gspca_dev, 0xa7, val); /* U saturation */
+ sccb_reg_write(gspca_dev, 0xa8, val); /* V saturation */
+ }
+}
+
static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1132,26 +1216,6 @@ static void sethvflip(struct gspca_dev *gspca_dev)
}
}
-static void setcolors(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 val;
- int i;
- static u8 color_tb[][6] = {
- {0x42, 0x42, 0x00, 0x11, 0x30, 0x41},
- {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52},
- {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66},
- {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80},
- {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a},
- {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8},
- {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd},
- };
-
- val = sd->ctrls[COLORS].val;
- for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++)
- sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]);
-}
-
static void setlightfreq(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1225,9 +1289,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
if ((sensor_id & 0xfff0) == 0x7670) {
sd->sensor = SENSOR_OV767x;
- gspca_dev->ctrl_dis = (1 << GAIN) |
+ gspca_dev->ctrl_dis = (1 << HUE) |
+ (1 << GAIN) |
(1 << AGC) |
(1 << SHARPNESS); /* auto */
+ sd->ctrls[SATURATION].min = 0,
+ sd->ctrls[SATURATION].max = 6,
+ sd->ctrls[SATURATION].def = 3,
sd->ctrls[BRIGHTNESS].min = -127;
sd->ctrls[BRIGHTNESS].max = 127;
sd->ctrls[BRIGHTNESS].def = 0;
@@ -1243,7 +1311,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode);
} else {
sd->sensor = SENSOR_OV772x;
- gspca_dev->ctrl_dis = (1 << COLORS);
gspca_dev->cam.bulk = 1;
gspca_dev->cam.bulk_size = 16384;
gspca_dev->cam.bulk_nurbs = 2;
@@ -1302,6 +1369,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
set_frame_rate(gspca_dev);
+ if (!(gspca_dev->ctrl_dis & (1 << HUE)))
+ sethue(gspca_dev);
+ setsaturation(gspca_dev);
if (!(gspca_dev->ctrl_dis & (1 << AGC)))
setagc(gspca_dev);
setawb(gspca_dev);
@@ -1314,8 +1384,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS)))
setsharpness(gspca_dev);
sethvflip(gspca_dev);
- if (!(gspca_dev->ctrl_dis & (1 << COLORS)))
- setcolors(gspca_dev);
setlightfreq(gspca_dev);
ov534_set_led(gspca_dev, 1);
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
index e6601b88603..b5797308a39 100644
--- a/drivers/media/video/gspca/ov534_9.c
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -1376,7 +1376,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
ARRAY_SIZE(ov562x_init_2));
reg_w(gspca_dev, 0xe0, 0x00);
} else {
- err("Unknown sensor %04x", sensor_id);
+ pr_err("Unknown sensor %04x", sensor_id);
return -EINVAL;
}
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
index 3844c49f269..fa661c6d6d5 100644
--- a/drivers/media/video/gspca/pac207.c
+++ b/drivers/media/video/gspca/pac207.c
@@ -29,6 +29,8 @@
#include <linux/input.h>
#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Pixart PAC207");
@@ -39,16 +41,17 @@ MODULE_LICENSE("GPL");
#define PAC207_BRIGHTNESS_MIN 0
#define PAC207_BRIGHTNESS_MAX 255
#define PAC207_BRIGHTNESS_DEFAULT 46
+#define PAC207_BRIGHTNESS_REG 0x08
#define PAC207_EXPOSURE_MIN 3
#define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */
#define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */
-#define PAC207_EXPOSURE_KNEE 9 /* fps: 90 / exposure -> 9: 10 fps */
+#define PAC207_EXPOSURE_REG 0x02
#define PAC207_GAIN_MIN 0
#define PAC207_GAIN_MAX 31
#define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */
-#define PAC207_GAIN_KNEE 15
+#define PAC207_GAIN_REG 0x0e
#define PAC207_AUTOGAIN_DEADZONE 30
@@ -56,13 +59,9 @@ MODULE_LICENSE("GPL");
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- u8 mode;
-
- u8 brightness;
- u8 exposure;
- u8 autogain;
- u8 gain;
+ struct v4l2_ctrl *brightness;
+ u8 mode;
u8 sof_read;
u8 header_read;
u8 autogain_ignore_frames;
@@ -70,80 +69,6 @@ struct sd {
atomic_t avg_lum;
};
-/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-#define SD_BRIGHTNESS 0
- {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = PAC207_BRIGHTNESS_MIN,
- .maximum = PAC207_BRIGHTNESS_MAX,
- .step = 1,
- .default_value = PAC207_BRIGHTNESS_DEFAULT,
- .flags = 0,
- },
- .set = sd_setbrightness,
- .get = sd_getbrightness,
- },
-#define SD_EXPOSURE 1
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = PAC207_EXPOSURE_MIN,
- .maximum = PAC207_EXPOSURE_MAX,
- .step = 1,
- .default_value = PAC207_EXPOSURE_DEFAULT,
- .flags = 0,
- },
- .set = sd_setexposure,
- .get = sd_getexposure,
- },
-#define SD_AUTOGAIN 2
- {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- .flags = 0,
- },
- .set = sd_setautogain,
- .get = sd_getautogain,
- },
-#define SD_GAIN 3
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = PAC207_GAIN_MIN,
- .maximum = PAC207_GAIN_MAX,
- .step = 1,
- .default_value = PAC207_GAIN_DEFAULT,
- .flags = 0,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
-};
-
static const struct v4l2_pix_format sif_mode[] = {
{176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE,
.bytesperline = 176,
@@ -167,39 +92,44 @@ static const __u8 pac207_sensor_init[][8] = {
{0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00},
};
-static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
+static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index,
const u8 *buffer, u16 length)
{
struct usb_device *udev = gspca_dev->dev;
int err;
+ if (gspca_dev->usb_err < 0)
+ return;
+
memcpy(gspca_dev->usb_buf, buffer, length);
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, index,
gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT);
- if (err < 0)
+ if (err < 0) {
pr_err("Failed to write registers to index 0x%04X, error %d\n",
index, err);
-
- return err;
+ gspca_dev->usb_err = err;
+ }
}
-
-static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
+static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value)
{
struct usb_device *udev = gspca_dev->dev;
int err;
+ if (gspca_dev->usb_err < 0)
+ return;
+
err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
value, index, NULL, 0, PAC207_CTRL_TIMEOUT);
- if (err)
+ if (err) {
pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n",
index, value, err);
-
- return err;
+ gspca_dev->usb_err = err;
+ }
}
static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
@@ -207,6 +137,9 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
struct usb_device *udev = gspca_dev->dev;
int res;
+ if (gspca_dev->usb_err < 0)
+ return 0;
+
res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, index,
@@ -214,7 +147,8 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
if (res < 0) {
pr_err("Failed to read a register (index 0x%04X, error %d)\n",
index, res);
- return res;
+ gspca_dev->usb_err = res;
+ return 0;
}
return gspca_dev->usb_buf[0];
@@ -224,7 +158,6 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index)
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
struct cam *cam;
u8 idreg[2];
@@ -247,10 +180,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->cam_mode = sif_mode;
cam->nmodes = ARRAY_SIZE(sif_mode);
- sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
- sd->exposure = PAC207_EXPOSURE_DEFAULT;
- sd->gain = PAC207_GAIN_DEFAULT;
- sd->autogain = AUTOGAIN_DEF;
return 0;
}
@@ -264,6 +193,87 @@ static int sd_init(struct gspca_dev *gspca_dev)
* Bit_2=Compression test mode enable */
pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
+ return gspca_dev->usb_err;
+}
+
+static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val)
+{
+ pac207_write_reg(gspca_dev, reg, val);
+ pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
+ pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
+}
+
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ gspca_dev->exposure->val = PAC207_EXPOSURE_DEFAULT;
+ gspca_dev->gain->val = PAC207_GAIN_DEFAULT;
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+ setcontrol(gspca_dev, PAC207_EXPOSURE_REG,
+ gspca_dev->exposure->val);
+ if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+ setcontrol(gspca_dev, PAC207_GAIN_REG,
+ gspca_dev->gain->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS,
+ PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX,
+ 1, PAC207_BRIGHTNESS_DEFAULT);
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX,
+ 1, PAC207_EXPOSURE_DEFAULT);
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN,
+ PAC207_GAIN_MIN, PAC207_GAIN_MAX,
+ 1, PAC207_GAIN_DEFAULT);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
return 0;
}
@@ -285,11 +295,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
else
pac207_write_reg(gspca_dev, 0x4a, 0x30);
pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */
- pac207_write_reg(gspca_dev, 0x08, sd->brightness);
+ pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness));
/* PGA global gain (Bit 4-0) */
- pac207_write_reg(gspca_dev, 0x0e, sd->gain);
- pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */
+ pac207_write_reg(gspca_dev, 0x0e,
+ v4l2_ctrl_g_ctrl(gspca_dev->gain));
+ pac207_write_reg(gspca_dev, 0x02,
+ v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */
mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
if (gspca_dev->width == 176) { /* 176x144 */
@@ -308,7 +320,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
sd->sof_read = 0;
sd->autogain_ignore_frames = 0;
atomic_set(&sd->avg_lum, -1);
- return 0;
+ return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
@@ -318,8 +330,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */
}
-/* Include pac common sof detection functions */
-#include "pac_common.h"
static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
{
@@ -331,9 +341,8 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
- else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum,
- 90, PAC207_AUTOGAIN_DEADZONE,
- PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE))
+ else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+ 90, PAC207_AUTOGAIN_DEADZONE))
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
}
@@ -384,118 +393,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static void setbrightness(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- pac207_write_reg(gspca_dev, 0x08, sd->brightness);
- pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
- pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
-}
-
-static void setexposure(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- pac207_write_reg(gspca_dev, 0x02, sd->exposure);
- pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
- pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
-}
-
-static void setgain(struct gspca_dev *gspca_dev)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- pac207_write_reg(gspca_dev, 0x0e, sd->gain);
- pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */
- pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */
-}
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightness(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return 0;
-}
-
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return 0;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
- /* when switching to autogain set defaults to make sure
- we are on a valid point of the autogain gain /
- exposure knee graph, and give this change time to
- take effect before doing autogain. */
- if (sd->autogain) {
- sd->exposure = PAC207_EXPOSURE_DEFAULT;
- sd->gain = PAC207_GAIN_DEFAULT;
- if (gspca_dev->streaming) {
- sd->autogain_ignore_frames =
- PAC_AUTOGAIN_IGNORE_FRAMES;
- setexposure(gspca_dev);
- setgain(gspca_dev);
- }
- }
-
- return 0;
-}
-
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
- return 0;
-}
-
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* interrupt packet data */
@@ -518,10 +415,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
.dq_callback = pac207_do_auto_gain,
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index 30662fccb0c..a0369a58c4b 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -23,43 +23,58 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-/* Some documentation about various registers as determined by trial and error.
-
- Register page 1:
-
- Address Description
- 0x78 Global control, bit 6 controls the LED (inverted)
-
- Register page 3:
-
- Address Description
- 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on
- the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
- 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
- 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
- 63 -> ~27 fps, the 2 msb's must always be 1 !!
- 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
- 1 -> ~30 fps, 2 -> ~20 fps
- 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time
- 0x0f Exposure bit 8, 0-448, 448 = no exposure at all
- 0x10 Master gain 0-31
- 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
-
- The registers are accessed in the following functions:
-
- Page | Register | Function
- -----+------------+---------------------------------------------------
- 0 | 0x0f..0x20 | setcolors()
- 0 | 0xa2..0xab | setbrightcont()
- 0 | 0xc5 | setredbalance()
- 0 | 0xc6 | setwhitebalance()
- 0 | 0xc7 | setbluebalance()
- 0 | 0xdc | setbrightcont(), setcolors()
- 3 | 0x02 | setexposure()
- 3 | 0x10 | setgain()
- 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
- 3 | 0x21 | sethvflip()
-*/
+/*
+ * Some documentation about various registers as determined by trial and error.
+ *
+ * Register page 1:
+ *
+ * Address Description
+ * 0x78 Global control, bit 6 controls the LED (inverted)
+ * 0x80 Compression balance, 2 interesting settings:
+ * 0x0f Default
+ * 0x50 Values >= this switch the camera to a lower compression,
+ * using the same table for both luminance and chrominance.
+ * This gives a sharper picture. Only usable when running
+ * at < 15 fps! Note currently the driver does not use this
+ * as the quality gain is small and the generated JPG-s are
+ * only understood by v4l-utils >= 0.8.9
+ *
+ * Register page 3:
+ *
+ * Address Description
+ * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on
+ * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
+ * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
+ * 63 -> ~27 fps, the 2 msb's must always be 1 !!
+ * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
+ * 1 -> ~30 fps, 2 -> ~20 fps
+ * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time
+ * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all
+ * 0x10 Gain 0-31
+ * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an
+ * amplification value of 1 rather then 0 at its lowest setting
+ * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+ * 0x80 Another framerate control, best left at 1, moving it from 1 to
+ * 2 causes the framerate to become 3/4th of what it was, and
+ * also seems to cause pixel averaging, resulting in an effective
+ * resolution of 320x240 and thus a much blockier image
+ *
+ * The registers are accessed in the following functions:
+ *
+ * Page | Register | Function
+ * -----+------------+---------------------------------------------------
+ * 0 | 0x0f..0x20 | setcolors()
+ * 0 | 0xa2..0xab | setbrightcont()
+ * 0 | 0xc5 | setredbalance()
+ * 0 | 0xc6 | setwhitebalance()
+ * 0 | 0xc7 | setbluebalance()
+ * 0 | 0xdc | setbrightcont(), setcolors()
+ * 3 | 0x02 | setexposure()
+ * 3 | 0x10, 0x12 | setgain()
+ * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
+ * 3 | 0x21 | sethvflip()
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -89,7 +104,6 @@ enum e_ctrl {
NCTRLS /* number of controls */
};
-/* specific webcam descriptor for pac7302 */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
@@ -198,10 +212,10 @@ static const struct ctrl sd_ctrls[] = {
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = 0,
- .maximum = 255,
+ .maximum = 62,
.step = 1,
-#define GAIN_DEF 127
-#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
+#define GAIN_DEF 15
+#define GAIN_KNEE 46
.default_value = GAIN_DEF,
},
.set_control = setgain
@@ -270,7 +284,6 @@ static const struct v4l2_pix_format vga_mode[] = {
#define LOAD_PAGE3 255
#define END_OF_SEQUENCE 0
-/* pac 7302 */
static const u8 init_7302[] = {
/* index,value */
0xff, 0x01, /* page 1 */
@@ -509,7 +522,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
return 0;
}
-/* This function is used by pac7302 only */
static void setbrightcont(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -536,7 +548,6 @@ static void setbrightcont(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0xdc, 0x01);
}
-/* This function is used by pac7302 only */
static void setcolors(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -590,9 +601,19 @@ static void setbluebalance(struct gspca_dev *gspca_dev)
static void setgain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ u8 reg10, reg12;
+
+ if (sd->ctrls[GAIN].val < 32) {
+ reg10 = sd->ctrls[GAIN].val;
+ reg12 = 0;
+ } else {
+ reg10 = 31;
+ reg12 = sd->ctrls[GAIN].val - 31;
+ }
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3);
+ reg_w(gspca_dev, 0x10, reg10);
+ reg_w(gspca_dev, 0x12, reg12);
/* load registers to sensor (Bit 0, auto clear) */
reg_w(gspca_dev, 0x11, 0x01);
@@ -604,28 +625,36 @@ static void setexposure(struct gspca_dev *gspca_dev)
u8 clockdiv;
u16 exposure;
- /* register 2 of frame 3 contains the clock divider configuring the
- no fps according to the formula: 90 / reg. sd->exposure is the
- desired exposure time in 0.5 ms. */
+ /*
+ * Register 2 of frame 3 contains the clock divider configuring the
+ * no fps according to the formula: 90 / reg. sd->exposure is the
+ * desired exposure time in 0.5 ms.
+ */
clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000;
- /* Note clockdiv = 3 also works, but when running at 30 fps, depending
- on the scene being recorded, the camera switches to another
- quantization table for certain JPEG blocks, and we don't know how
- to decompress these blocks. So we cap the framerate at 15 fps */
+ /*
+ * Note clockdiv = 3 also works, but when running at 30 fps, depending
+ * on the scene being recorded, the camera switches to another
+ * quantization table for certain JPEG blocks, and we don't know how
+ * to decompress these blocks. So we cap the framerate at 15 fps.
+ */
if (clockdiv < 6)
clockdiv = 6;
else if (clockdiv > 63)
clockdiv = 63;
- /* reg2 MUST be a multiple of 3, except when between 6 and 12?
- Always round up, otherwise we cannot get the desired frametime
- using the partial frame time exposure control */
+ /*
+ * Register 2 MUST be a multiple of 3, except when between 6 and 12?
+ * Always round up, otherwise we cannot get the desired frametime
+ * using the partial frame time exposure control.
+ */
if (clockdiv < 6 || clockdiv > 12)
clockdiv = ((clockdiv + 2) / 3) * 3;
- /* frame exposure time in ms = 1000 * clockdiv / 90 ->
- exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */
+ /*
+ * frame exposure time in ms = 1000 * clockdiv / 90 ->
+ * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90)
+ */
exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv);
/* 0 = use full frametime, 448 = no exposure, reverse it */
exposure = 448 - exposure;
@@ -643,10 +672,12 @@ static void setautogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- /* when switching to autogain set defaults to make sure
- we are on a valid point of the autogain gain /
- exposure knee graph, and give this change time to
- take effect before doing autogain. */
+ /*
+ * When switching to autogain set defaults to make sure
+ * we are on a valid point of the autogain gain /
+ * exposure knee graph, and give this change time to
+ * take effect before doing autogain.
+ */
if (sd->ctrls[AUTOGAIN].val) {
sd->ctrls[EXPOSURE].val = EXPOSURE_DEF;
sd->ctrls[GAIN].val = GAIN_DEF;
@@ -700,8 +731,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
setautogain(gspca_dev);
sethvflip(gspca_dev);
- /* only resolution 640x480 is supported for pac7302 */
-
sd->sof_read = 0;
atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val);
@@ -729,9 +758,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x78, 0x40);
}
-/* !! coarse_grained_expo_autogain is not used !! */
-#define exp_too_low_cnt flags
-#define exp_too_high_cnt sof_read
+#define WANT_REGULAR_AUTOGAIN
#include "autogain_functions.h"
static void do_autogain(struct gspca_dev *gspca_dev)
@@ -792,10 +819,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
if (sof) {
int n, lum_offset, footer_length;
- /* 6 bytes after the FF D9 EOF marker a number of lumination
- bytes are send corresponding to different parts of the
- image, the 14th and 15th byte after the EOF seem to
- correspond to the center of the image */
+ /*
+ * 6 bytes after the FF D9 EOF marker a number of lumination
+ * bytes are send corresponding to different parts of the
+ * image, the 14th and 15th byte after the EOF seem to
+ * correspond to the center of the image.
+ */
lum_offset = 61 + sizeof pac_sof_marker;
footer_length = 74;
@@ -839,9 +868,10 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
u8 index;
u8 value;
- /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
- long on the USB bus)
- */
+ /*
+ * reg->reg: bit0..15: reserved for register index (wIndex is 16bit
+ * long on the USB bus)
+ */
if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
reg->match.addr == 0 &&
(reg->reg < 0x000000ff) &&
@@ -852,9 +882,11 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
index = reg->reg;
value = reg->val;
- /* Note that there shall be no access to other page
- by any other function between the page swith and
- the actual register write */
+ /*
+ * Note that there shall be no access to other page
+ * by any other function between the page switch and
+ * the actual register write.
+ */
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
reg_w(gspca_dev, index, value);
@@ -940,6 +972,7 @@ static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
{USB_DEVICE(0x093a, 0x2625)},
{USB_DEVICE(0x093a, 0x2626)},
+ {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP},
{USB_DEVICE(0x093a, 0x2628)},
{USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
{USB_DEVICE(0x093a, 0x262a)},
diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
index 1ac111176ff..2cb7d95f7be 100644
--- a/drivers/media/video/gspca/pac7311.c
+++ b/drivers/media/video/gspca/pac7311.c
@@ -20,34 +20,42 @@
*/
/* Some documentation about various registers as determined by trial and error.
- When the register addresses differ between the 7202 and the 7311 the 2
- different addresses are written as 7302addr/7311addr, when one of the 2
- addresses is a - sign that register description is not valid for the
- matching IC.
-
- Register page 1:
-
- Address Description
- -/0x08 Unknown compressor related, must always be 8 except when not
- in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
- -/0x1b Auto white balance related, bit 0 is AWB enable (inverted)
- bits 345 seem to toggle per color gains on/off (inverted)
- 0x78 Global control, bit 6 controls the LED (inverted)
- -/0x80 JPEG compression ratio ? Best not touched
-
- Register page 3/4:
-
- Address Description
- 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
- the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
- -/0x0f Master gain 1-245, low value = high gain
- 0x10/- Master gain 0-31
- -/0x10 Another gain 0-15, limited influence (1-2x gain I guess)
- 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
- -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to
- completely disable the analog amplification block. Set to 0x68
- for max gain, 0x14 for minimal gain.
-*/
+ *
+ * Register page 1:
+ *
+ * Address Description
+ * 0x08 Unknown compressor related, must always be 8 except when not
+ * in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
+ * 0x1b Auto white balance related, bit 0 is AWB enable (inverted)
+ * bits 345 seem to toggle per color gains on/off (inverted)
+ * 0x78 Global control, bit 6 controls the LED (inverted)
+ * 0x80 Compression balance, interesting settings:
+ * 0x01 Use this to allow the camera to switch to higher compr.
+ * on the fly. Needed to stay within bandwidth @ 640x480@30
+ * 0x1c From usb captures under Windows for 640x480
+ * 0x2a Values >= this switch the camera to a lower compression,
+ * using the same table for both luminance and chrominance.
+ * This gives a sharper picture. Usable only at 640x480@ <
+ * 15 fps or 320x240 / 160x120. Note currently the driver
+ * does not use this as the quality gain is small and the
+ * generated JPG-s are only understood by v4l-utils >= 0.8.9
+ * 0x3f From usb captures under Windows for 320x240
+ * 0x69 From usb captures under Windows for 160x120
+ *
+ * Register page 4:
+ *
+ * Address Description
+ * 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
+ * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
+ * 0x0f Master gain 1-245, low value = high gain
+ * 0x10 Another gain 0-15, limited influence (1-2x gain I guess)
+ * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
+ * Note setting vflip disabled leads to a much lower image quality,
+ * so we always vflip, and tell userspace to flip it back
+ * 0x27 Seems to toggle various gains on / off, Setting bit 7 seems to
+ * completely disable the analog amplification block. Set to 0x68
+ * for max gain, 0x14 for minimal gain.
+ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -55,21 +63,21 @@
#include <linux/input.h>
#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
+
+#define PAC7311_GAIN_DEFAULT 122
+#define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */
MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
MODULE_DESCRIPTION("Pixart PAC7311");
MODULE_LICENSE("GPL");
-/* specific webcam descriptor for pac7311 */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char contrast;
- unsigned char gain;
- unsigned char exposure;
- unsigned char autogain;
- __u8 hflip;
- __u8 vflip;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *hflip;
u8 sof_read;
u8 autogain_ignore_frames;
@@ -77,114 +85,6 @@ struct sd {
atomic_t avg_lum;
};
-/* V4L2 controls supported by the driver */
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_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_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
-
-static const struct ctrl sd_ctrls[] = {
-/* This control is for both the 7302 and the 7311 */
- {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
-#define CONTRAST_MAX 255
- .maximum = CONTRAST_MAX,
- .step = 1,
-#define CONTRAST_DEF 127
- .default_value = CONTRAST_DEF,
- },
- .set = sd_setcontrast,
- .get = sd_getcontrast,
- },
-/* All controls below are for both the 7302 and the 7311 */
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
-#define GAIN_MAX 255
- .maximum = GAIN_MAX,
- .step = 1,
-#define GAIN_DEF 127
-#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
- .default_value = GAIN_DEF,
- },
- .set = sd_setgain,
- .get = sd_getgain,
- },
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
-#define EXPOSURE_MAX 255
- .maximum = EXPOSURE_MAX,
- .step = 1,
-#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */
-#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
- .default_value = EXPOSURE_DEF,
- },
- .set = sd_setexposure,
- .get = sd_getexposure,
- },
- {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define AUTOGAIN_DEF 1
- .default_value = AUTOGAIN_DEF,
- },
- .set = sd_setautogain,
- .get = sd_getautogain,
- },
- {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Mirror",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
-#define HFLIP_DEF 0
- .default_value = HFLIP_DEF,
- },
- .set = sd_sethflip,
- .get = sd_gethflip,
- },
- {
- {
- .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,
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -206,8 +106,8 @@ static const struct v4l2_pix_format vga_mode[] = {
#define LOAD_PAGE4 254
#define END_OF_SEQUENCE 0
-/* pac 7311 */
static const __u8 init_7311[] = {
+ 0xff, 0x01,
0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */
0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */
0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */
@@ -387,90 +287,73 @@ static void reg_w_var(struct gspca_dev *gspca_dev,
static int sd_config(struct gspca_dev *gspca_dev,
const struct usb_device_id *id)
{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
+ struct cam *cam = &gspca_dev->cam;
- cam = &gspca_dev->cam;
-
- PDEBUG(D_CONF, "Find Sensor PAC7311");
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
+ cam->input_flags = V4L2_IN_ST_VFLIP;
- sd->contrast = CONTRAST_DEF;
- sd->gain = GAIN_DEF;
- sd->exposure = EXPOSURE_DEF;
- sd->autogain = AUTOGAIN_DEF;
- sd->hflip = HFLIP_DEF;
- sd->vflip = VFLIP_DEF;
return 0;
}
-/* This function is used by pac7311 only */
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
reg_w(gspca_dev, 0xff, 0x04);
- reg_w(gspca_dev, 0x10, sd->contrast >> 4);
+ reg_w(gspca_dev, 0x10, val);
/* load registers to sensor (Bit 0, auto clear) */
reg_w(gspca_dev, 0x11, 0x01);
}
-static void setgain(struct gspca_dev *gspca_dev)
+static void setgain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- int gain = GAIN_MAX - sd->gain;
-
- if (gain < 1)
- gain = 1;
- else if (gain > 245)
- gain = 245;
reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
reg_w(gspca_dev, 0x0e, 0x00);
- reg_w(gspca_dev, 0x0f, gain);
+ reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1);
/* load registers to sensor (Bit 0, auto clear) */
reg_w(gspca_dev, 0x11, 0x01);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- __u8 reg;
-
- /* register 2 of frame 3/4 contains the clock divider configuring the
- no fps according to the formula: 60 / reg. sd->exposure is the
- desired exposure time in ms. */
- reg = 120 * sd->exposure / 1000;
- if (reg < 2)
- reg = 2;
- else if (reg > 63)
- reg = 63;
-
reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
- reg_w(gspca_dev, 0x02, reg);
+ reg_w(gspca_dev, 0x02, val);
- /* Page 1 register 8 must always be 0x08 except when not in
- 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+
+ /*
+ * Page 1 register 8 must always be 0x08 except when not in
+ * 640x480 mode and page 4 reg 2 <= 3 then it must be 9
+ */
reg_w(gspca_dev, 0xff, 0x01);
- if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
- reg <= 3) {
+ if (gspca_dev->width != 640 && val <= 3)
reg_w(gspca_dev, 0x08, 0x09);
- } else {
+ else
reg_w(gspca_dev, 0x08, 0x08);
- }
+
+ /*
+ * Page1 register 80 sets the compression balance, normally we
+ * want / use 0x1c, but for 640x480@30fps we must allow the
+ * camera to use higher compression or we may run out of
+ * bandwidth.
+ */
+ if (gspca_dev->width == 640 && val == 2)
+ reg_w(gspca_dev, 0x80, 0x01);
+ else
+ reg_w(gspca_dev, 0x80, 0x1c);
/* load registers to sensor (Bit 0, auto clear) */
reg_w(gspca_dev, 0x11, 0x01);
}
-static void sethvflip(struct gspca_dev *gspca_dev)
+static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
{
- struct sd *sd = (struct sd *) gspca_dev;
__u8 data;
reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
- data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00);
+ data = (hflip ? 0x04 : 0x00) |
+ (vflip ? 0x08 : 0x00);
reg_w(gspca_dev, 0x21, data);
/* load registers to sensor (Bit 0, auto clear) */
@@ -484,6 +367,82 @@ static int sd_init(struct gspca_dev *gspca_dev)
return gspca_dev->usb_err;
}
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT;
+ gspca_dev->gain->val = PAC7311_GAIN_DEFAULT;
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ setcontrast(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
+ setexposure(gspca_dev, gspca_dev->exposure->val);
+ if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
+ setgain(gspca_dev, gspca_dev->gain->val);
+ break;
+ case V4L2_CID_HFLIP:
+ sethvflip(gspca_dev, sd->hflip->val, 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+/* this function is called at probe time */
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 15, 1, 7);
+ gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 2, 63, 1,
+ PAC7311_EXPOSURE_DEFAULT);
+ gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 244, 1,
+ PAC7311_GAIN_DEFAULT);
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+
+ v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
+ return 0;
+}
+
+/* -- start the camera -- */
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -492,19 +451,19 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w_var(gspca_dev, start_7311,
page4_7311, sizeof(page4_7311));
- setcontrast(gspca_dev);
- setgain(gspca_dev);
- setexposure(gspca_dev);
- sethvflip(gspca_dev);
+ setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
+ setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain));
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
+ sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1);
/* set correct resolution */
switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
- case 2: /* 160x120 pac7311 */
+ case 2: /* 160x120 */
reg_w(gspca_dev, 0xff, 0x01);
reg_w(gspca_dev, 0x17, 0x20);
reg_w(gspca_dev, 0x87, 0x10);
break;
- case 1: /* 320x240 pac7311 */
+ case 1: /* 320x240 */
reg_w(gspca_dev, 0xff, 0x01);
reg_w(gspca_dev, 0x17, 0x30);
reg_w(gspca_dev, 0x87, 0x11);
@@ -541,14 +500,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
}
-/* called on streamoff with alt 0 and on disconnect for 7311 */
-static void sd_stop0(struct gspca_dev *gspca_dev)
-{
-}
-
-/* Include pac common sof detection functions */
-#include "pac_common.h"
-
static void do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -558,13 +509,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
if (avg_lum == -1)
return;
- desired_lum = 200;
+ desired_lum = 170;
deadzone = 20;
if (sd->autogain_ignore_frames > 0)
sd->autogain_ignore_frames--;
- else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
- deadzone, GAIN_KNEE, EXPOSURE_KNEE))
+ else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
+ desired_lum, deadzone))
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
}
@@ -628,10 +579,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
if (sof) {
int n, lum_offset, footer_length;
- /* 6 bytes after the FF D9 EOF marker a number of lumination
- bytes are send corresponding to different parts of the
- image, the 14th and 15th byte after the EOF seem to
- correspond to the center of the image */
+ /*
+ * 6 bytes after the FF D9 EOF marker a number of lumination
+ * bytes are send corresponding to different parts of the
+ * image, the 14th and 15th byte after the EOF seem to
+ * correspond to the center of the image.
+ */
lum_offset = 24 + sizeof pac_sof_marker;
footer_length = 26;
@@ -668,127 +621,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setcontrast(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
- /* when switching to autogain set defaults to make sure
- we are on a valid point of the autogain gain /
- exposure knee graph, and give this change time to
- take effect before doing autogain. */
- if (sd->autogain) {
- sd->exposure = EXPOSURE_DEF;
- sd->gain = GAIN_DEF;
- if (gspca_dev->streaming) {
- sd->autogain_ignore_frames =
- PAC_AUTOGAIN_IGNORE_FRAMES;
- setexposure(gspca_dev);
- setgain(gspca_dev);
- }
- }
-
- return gspca_dev->usb_err;
-}
-
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
- return 0;
-}
-
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hflip = val;
- if (gspca_dev->streaming)
- sethvflip(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hflip;
- return 0;
-}
-
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->vflip = val;
- if (gspca_dev->streaming)
- sethvflip(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->vflip;
- return 0;
-}
-
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* interrupt packet data */
@@ -820,16 +652,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
}
#endif
-/* sub-driver description for pac7311 */
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.start = sd_start,
.stopN = sd_stopN,
- .stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = do_autogain,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index 7e71aa2d252..ad098202d7f 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -59,35 +59,38 @@ MODULE_LICENSE("GPL");
#define SENSOR_MT9M111 9
#define SENSOR_MT9M112 10
#define SENSOR_HV7131R 11
-#define SENSOR_MT9VPRB 20
+#define SENSOR_MT9VPRB 12
/* camera flags */
#define HAS_NO_BUTTON 0x1
#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */
#define FLIP_DETECT 0x4
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- SATURATION,
- HUE,
- GAMMA,
- BLUE,
- RED,
- VFLIP,
- HFLIP,
- EXPOSURE,
- GAIN,
- AUTOGAIN,
- QUALITY,
- NCTRLS /* number of controls */
-};
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev;
- struct gspca_ctrl ctrls[NCTRLS];
+ struct { /* color control cluster */
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ struct v4l2_ctrl *saturation;
+ struct v4l2_ctrl *hue;
+ };
+ struct { /* blue/red balance control cluster */
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *red;
+ };
+ struct { /* h/vflip control cluster */
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+ struct v4l2_ctrl *gamma;
+ struct { /* autogain and exposure or gain control cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *gain;
+ };
+ struct v4l2_ctrl *jpegqual;
struct work_struct work;
struct workqueue_struct *work_thread;
@@ -105,6 +108,7 @@ struct sd {
u8 exposure_step;
u8 i2c_addr;
+ u8 i2c_intf;
u8 sensor;
u8 hstart;
u8 vstart;
@@ -166,175 +170,6 @@ static const struct dmi_system_id flip_dmi_table[] = {
{}
};
-static void set_cmatrix(struct gspca_dev *gspca_dev);
-static void set_gamma(struct gspca_dev *gspca_dev);
-static void set_redblue(struct gspca_dev *gspca_dev);
-static void set_hvflip(struct gspca_dev *gspca_dev);
-static void set_exposure(struct gspca_dev *gspca_dev);
-static void set_gain(struct gspca_dev *gspca_dev);
-static void set_quality(struct gspca_dev *gspca_dev);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f
- },
- .set_control = set_cmatrix
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f
- },
- .set_control = set_cmatrix
- },
-[SATURATION] = {
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x7f
- },
- .set_control = set_cmatrix
- },
-[HUE] = {
- {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -180,
- .maximum = 180,
- .step = 1,
- .default_value = 0
- },
- .set_control = set_cmatrix
- },
-[GAMMA] = {
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0x10
- },
- .set_control = set_gamma
- },
-[BLUE] = {
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Balance",
- .minimum = 0,
- .maximum = 0x7f,
- .step = 1,
- .default_value = 0x28
- },
- .set_control = set_redblue
- },
-[RED] = {
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Balance",
- .minimum = 0,
- .maximum = 0x7f,
- .step = 1,
- .default_value = 0x28
- },
- .set_control = set_redblue
- },
-[HFLIP] = {
- {
- .id = V4L2_CID_HFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Horizontal Flip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = set_hvflip
- },
-[VFLIP] = {
- {
- .id = V4L2_CID_VFLIP,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Vertical Flip",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- .set_control = set_hvflip
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
- .maximum = 0x1780,
- .step = 1,
- .default_value = 0x33,
- },
- .set_control = set_exposure
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 28,
- .step = 1,
- .default_value = 0,
- },
- .set_control = set_gain
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Exposure",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- },
-[QUALITY] = {
- {
- .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Compression Quality",
-#define QUALITY_MIN 50
-#define QUALITY_MAX 90
-#define QUALITY_DEF 80
- .minimum = QUALITY_MIN,
- .maximum = QUALITY_MAX,
- .step = 1,
- .default_value = QUALITY_DEF,
- },
- .set_control = set_quality
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 160,
@@ -747,7 +582,7 @@ static const s16 hsv_blue_y[] = {
4, 2, 0, -1, -3, -5, -7, -9, -11
};
-static u16 i2c_ident[] = {
+static const u16 i2c_ident[] = {
V4L2_IDENT_OV9650,
V4L2_IDENT_OV9655,
V4L2_IDENT_SOI968,
@@ -760,9 +595,10 @@ static u16 i2c_ident[] = {
V4L2_IDENT_MT9M111,
V4L2_IDENT_MT9M112,
V4L2_IDENT_HV7131R,
+[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN,
};
-static u16 bridge_init[][2] = {
+static const u16 bridge_init[][2] = {
{0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c},
{0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40},
{0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10},
@@ -786,7 +622,7 @@ static u16 bridge_init[][2] = {
};
/* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */
-static u8 ov_gain[] = {
+static const u8 ov_gain[] = {
0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */,
0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */,
0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */,
@@ -798,7 +634,7 @@ static u8 ov_gain[] = {
};
/* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */
-static u16 micron1_gain[] = {
+static const u16 micron1_gain[] = {
/* 1x 1.25x 1.5x 1.75x */
0x0020, 0x0028, 0x0030, 0x0038,
/* 2x 2.25x 2.5x 2.75x */
@@ -819,7 +655,7 @@ static u16 micron1_gain[] = {
/* mt9m001 sensor uses a different gain formula then other micron sensors */
/* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */
-static u16 micron2_gain[] = {
+static const u16 micron2_gain[] = {
/* 1x 1.25x 1.5x 1.75x */
0x0008, 0x000a, 0x000c, 0x000e,
/* 2x 2.25x 2.5x 2.75x */
@@ -839,7 +675,7 @@ static u16 micron2_gain[] = {
};
/* Gain = .5 + bit[7:0] / 16 */
-static u8 hv7131r_gain[] = {
+static const u8 hv7131r_gain[] = {
0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */,
0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */,
0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */,
@@ -850,7 +686,7 @@ static u8 hv7131r_gain[] = {
0x78 /* 8x */
};
-static struct i2c_reg_u8 soi968_init[] = {
+static const struct i2c_reg_u8 soi968_init[] = {
{0x0c, 0x00}, {0x0f, 0x1f},
{0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
{0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
@@ -864,7 +700,7 @@ static struct i2c_reg_u8 soi968_init[] = {
{0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80},
};
-static struct i2c_reg_u8 ov7660_init[] = {
+static const struct i2c_reg_u8 ov7660_init[] = {
{0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3},
{0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40},
{0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a},
@@ -872,11 +708,11 @@ static struct i2c_reg_u8 ov7660_init[] = {
0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */
{0x17, 0x10}, {0x18, 0x61},
{0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43},
- {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6},
- {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50},
+ {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0x00},
+ {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50},
};
-static struct i2c_reg_u8 ov7670_init[] = {
+static const struct i2c_reg_u8 ov7670_init[] = {
{0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
{0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
{0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
@@ -933,7 +769,7 @@ static struct i2c_reg_u8 ov7670_init[] = {
{0x93, 0x00},
};
-static struct i2c_reg_u8 ov9650_init[] = {
+static const struct i2c_reg_u8 ov9650_init[] = {
{0x00, 0x00}, {0x01, 0x78},
{0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
{0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
@@ -963,7 +799,7 @@ static struct i2c_reg_u8 ov9650_init[] = {
{0xaa, 0x92}, {0xab, 0x0a},
};
-static struct i2c_reg_u8 ov9655_init[] = {
+static const struct i2c_reg_u8 ov9655_init[] = {
{0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
{0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08},
{0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d},
@@ -990,7 +826,7 @@ static struct i2c_reg_u8 ov9655_init[] = {
{0x04, 0x03}, {0x00, 0x13},
};
-static struct i2c_reg_u16 mt9v112_init[] = {
+static const struct i2c_reg_u16 mt9v112_init[] = {
{0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020},
{0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b},
{0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001},
@@ -1009,7 +845,7 @@ static struct i2c_reg_u16 mt9v112_init[] = {
{0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae},
};
-static struct i2c_reg_u16 mt9v111_init[] = {
+static const struct i2c_reg_u16 mt9v111_init[] = {
{0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000},
{0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0},
{0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e},
@@ -1019,7 +855,7 @@ static struct i2c_reg_u16 mt9v111_init[] = {
{0x0e, 0x0008}, {0x20, 0x0000}
};
-static struct i2c_reg_u16 mt9v011_init[] = {
+static const struct i2c_reg_u16 mt9v011_init[] = {
{0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000},
{0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1},
{0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006},
@@ -1046,7 +882,7 @@ static struct i2c_reg_u16 mt9v011_init[] = {
{0x06, 0x0029}, {0x05, 0x0009},
};
-static struct i2c_reg_u16 mt9m001_init[] = {
+static const struct i2c_reg_u16 mt9m001_init[] = {
{0x0d, 0x0001},
{0x0d, 0x0000},
{0x04, 0x0500}, /* hres = 1280 */
@@ -1062,21 +898,21 @@ static struct i2c_reg_u16 mt9m001_init[] = {
{0x35, 0x0057},
};
-static struct i2c_reg_u16 mt9m111_init[] = {
+static const struct i2c_reg_u16 mt9m111_init[] = {
{0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
{0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
{0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
{0xf0, 0x0000},
};
-static struct i2c_reg_u16 mt9m112_init[] = {
+static const struct i2c_reg_u16 mt9m112_init[] = {
{0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008},
{0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300},
{0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e},
{0xf0, 0x0000},
};
-static struct i2c_reg_u8 hv7131r_init[] = {
+static const struct i2c_reg_u8 hv7131r_init[] = {
{0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08},
{0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0},
{0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08},
@@ -1167,7 +1003,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
* from the point of view of the bridge, the length
* includes the address
*/
- row[0] = 0x81 | (2 << 4);
+ row[0] = sd->i2c_intf | (2 << 4);
row[1] = sd->i2c_addr;
row[2] = reg;
row[3] = val;
@@ -1180,7 +1016,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
}
static void i2c_w1_buf(struct gspca_dev *gspca_dev,
- struct i2c_reg_u8 *buf, int sz)
+ const struct i2c_reg_u8 *buf, int sz)
{
while (--sz >= 0) {
i2c_w1(gspca_dev, buf->reg, buf->val);
@@ -1197,7 +1033,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
* from the point of view of the bridge, the length
* includes the address
*/
- row[0] = 0x81 | (3 << 4);
+ row[0] = sd->i2c_intf | (3 << 4);
row[1] = sd->i2c_addr;
row[2] = reg;
row[3] = val >> 8;
@@ -1210,7 +1046,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
}
static void i2c_w2_buf(struct gspca_dev *gspca_dev,
- struct i2c_reg_u16 *buf, int sz)
+ const struct i2c_reg_u16 *buf, int sz)
{
while (--sz >= 0) {
i2c_w2(gspca_dev, buf->reg, buf->val);
@@ -1223,7 +1059,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
struct sd *sd = (struct sd *) gspca_dev;
u8 row[8];
- row[0] = 0x81 | (1 << 4);
+ row[0] = sd->i2c_intf | (1 << 4);
row[1] = sd->i2c_addr;
row[2] = reg;
row[3] = 0;
@@ -1232,7 +1068,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
row[6] = 0;
row[7] = 0x10;
i2c_w(gspca_dev, row);
- row[0] = 0x81 | (1 << 4) | 0x02;
+ row[0] = sd->i2c_intf | (1 << 4) | 0x02;
row[2] = 0;
i2c_w(gspca_dev, row);
reg_r(gspca_dev, 0x10c2, 5);
@@ -1244,7 +1080,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
struct sd *sd = (struct sd *) gspca_dev;
u8 row[8];
- row[0] = 0x81 | (1 << 4);
+ row[0] = sd->i2c_intf | (1 << 4);
row[1] = sd->i2c_addr;
row[2] = reg;
row[3] = 0;
@@ -1253,7 +1089,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
row[6] = 0;
row[7] = 0x10;
i2c_w(gspca_dev, row);
- row[0] = 0x81 | (2 << 4) | 0x02;
+ row[0] = sd->i2c_intf | (2 << 4) | 0x02;
row[2] = 0;
i2c_w(gspca_dev, row);
reg_r(gspca_dev, 0x10c2, 5);
@@ -1294,8 +1130,6 @@ static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_err < 0)
pr_err("OV9655 sensor initialization failed\n");
- /* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
sd->hstart = 1;
sd->vstart = 2;
}
@@ -1310,9 +1144,6 @@ static void soi968_init_sensor(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_err < 0)
pr_err("SOI968 sensor initialization failed\n");
- /* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP)
- | (1 << EXPOSURE);
sd->hstart = 60;
sd->vstart = 11;
}
@@ -1340,8 +1171,6 @@ static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_err < 0)
pr_err("OV7670 sensor initialization failed\n");
- /* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
sd->hstart = 0;
sd->vstart = 1;
}
@@ -1378,9 +1207,6 @@ static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
pr_err("MT9V111 sensor initialization failed\n");
return;
}
- gspca_dev->ctrl_dis = (1 << EXPOSURE)
- | (1 << AUTOGAIN)
- | (1 << GAIN);
sd->hstart = 2;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V111;
@@ -1422,8 +1248,6 @@ static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_err < 0)
pr_err("MT9M112 sensor initialization failed\n");
- gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
- | (1 << GAIN);
sd->hstart = 0;
sd->vstart = 2;
}
@@ -1436,8 +1260,6 @@ static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_err < 0)
pr_err("MT9M111 sensor initialization failed\n");
- gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
- | (1 << GAIN);
sd->hstart = 0;
sd->vstart = 2;
}
@@ -1470,8 +1292,6 @@ static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
if (gspca_dev->usb_err < 0)
pr_err("MT9M001 sensor initialization failed\n");
- /* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
sd->hstart = 1;
sd->vstart = 1;
}
@@ -1488,20 +1308,18 @@ static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
sd->vstart = 1;
}
-static void set_cmatrix(struct gspca_dev *gspca_dev)
+static void set_cmatrix(struct gspca_dev *gspca_dev,
+ s32 brightness, s32 contrast, s32 satur, s32 hue)
{
- struct sd *sd = (struct sd *) gspca_dev;
- int satur;
- s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val;
+ s32 hue_coord, hue_index = 180 + hue;
u8 cmatrix[21];
memset(cmatrix, 0, sizeof cmatrix);
- cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26;
+ cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26;
cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
- cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80;
+ cmatrix[18] = brightness - 0x80;
- satur = sd->ctrls[SATURATION].val;
hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
cmatrix[6] = hue_coord;
cmatrix[7] = (hue_coord >> 8) & 0x0f;
@@ -1529,11 +1347,10 @@ static void set_cmatrix(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x10e1, cmatrix, 21);
}
-static void set_gamma(struct gspca_dev *gspca_dev)
+static void set_gamma(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
u8 gamma[17];
- u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100;
+ u8 gval = val * 0xb8 / 0x100;
gamma[0] = 0x0a;
gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
@@ -1556,26 +1373,21 @@ static void set_gamma(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x1190, gamma, 17);
}
-static void set_redblue(struct gspca_dev *gspca_dev)
+static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val);
- reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val);
+ reg_w1(gspca_dev, 0x118c, red);
+ reg_w1(gspca_dev, 0x118f, blue);
}
-static void set_hvflip(struct gspca_dev *gspca_dev)
+static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
{
- u8 value, tslb, hflip, vflip;
+ u8 value, tslb;
u16 value2;
struct sd *sd = (struct sd *) gspca_dev;
if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
- hflip = !sd->ctrls[HFLIP].val;
- vflip = !sd->ctrls[VFLIP].val;
- } else {
- hflip = sd->ctrls[HFLIP].val;
- vflip = sd->ctrls[VFLIP].val;
+ hflip = !hflip;
+ vflip = !vflip;
}
switch (sd->sensor) {
@@ -1638,20 +1450,38 @@ static void set_hvflip(struct gspca_dev *gspca_dev)
}
}
-static void set_exposure(struct gspca_dev *gspca_dev)
+static void set_exposure(struct gspca_dev *gspca_dev, s32 expo)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e};
- int expo;
+ u8 exp[8] = {sd->i2c_intf, sd->i2c_addr,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+ int expo2;
+
+ if (gspca_dev->streaming)
+ exp[7] = 0x1e;
- expo = sd->ctrls[EXPOSURE].val;
switch (sd->sensor) {
case SENSOR_OV7660:
case SENSOR_OV7670:
case SENSOR_OV9655:
case SENSOR_OV9650:
+ if (expo > 547)
+ expo2 = 547;
+ else
+ expo2 = expo;
+ exp[0] |= (2 << 4);
+ exp[2] = 0x10; /* AECH */
+ exp[3] = expo2 >> 2;
+ exp[7] = 0x10;
+ i2c_w(gspca_dev, exp);
+ exp[2] = 0x04; /* COM1 */
+ exp[3] = expo2 & 0x0003;
+ exp[7] = 0x10;
+ i2c_w(gspca_dev, exp);
+ expo -= expo2;
+ exp[7] = 0x1e;
exp[0] |= (3 << 4);
- exp[2] = 0x2d;
+ exp[2] = 0x2d; /* ADVFL & ADVFH */
exp[3] = expo;
exp[4] = expo >> 8;
break;
@@ -1676,13 +1506,15 @@ static void set_exposure(struct gspca_dev *gspca_dev)
i2c_w(gspca_dev, exp);
}
-static void set_gain(struct gspca_dev *gspca_dev)
+static void set_gain(struct gspca_dev *gspca_dev, s32 g)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d};
- int g;
+ u8 gain[8] = {sd->i2c_intf, sd->i2c_addr,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
+
+ if (gspca_dev->streaming)
+ gain[7] = 0x15; /* or 1d ? */
- g = sd->ctrls[GAIN].val;
switch (sd->sensor) {
case SENSOR_OV7660:
case SENSOR_OV7670:
@@ -1721,11 +1553,11 @@ static void set_gain(struct gspca_dev *gspca_dev)
i2c_w(gspca_dev, gain);
}
-static void set_quality(struct gspca_dev *gspca_dev)
+static void set_quality(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+ jpeg_set_qual(sd->jpeg_hdr, val);
reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */
reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
@@ -1827,6 +1659,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor = id->driver_info >> 8;
sd->i2c_addr = id->driver_info;
sd->flags = id->driver_info >> 16;
+ sd->i2c_intf = 0x80; /* i2c 100 Kb/s */
switch (sd->sensor) {
case SENSOR_MT9M112:
@@ -1840,6 +1673,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam->cam_mode = mono_mode;
cam->nmodes = ARRAY_SIZE(mono_mode);
break;
+ case SENSOR_HV7131R:
+ sd->i2c_intf = 0x81; /* i2c 400 Kb/s */
+ /* fall thru */
default:
cam->cam_mode = vga_mode;
cam->nmodes = ARRAY_SIZE(vga_mode);
@@ -1850,13 +1686,133 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->older_step = 0;
sd->exposure_step = 16;
- gspca_dev->cam.ctrls = sd->ctrls;
-
INIT_WORK(&sd->work, qual_upd);
return 0;
}
+static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ gspca_dev->usb_err = 0;
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ /* color control cluster */
+ case V4L2_CID_BRIGHTNESS:
+ set_cmatrix(gspca_dev, sd->brightness->val,
+ sd->contrast->val, sd->saturation->val, sd->hue->val);
+ break;
+ case V4L2_CID_GAMMA:
+ set_gamma(gspca_dev, ctrl->val);
+ break;
+ /* blue/red balance cluster */
+ case V4L2_CID_BLUE_BALANCE:
+ set_redblue(gspca_dev, sd->blue->val, sd->red->val);
+ break;
+ /* h/vflip cluster */
+ case V4L2_CID_HFLIP:
+ set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val);
+ break;
+ /* standalone exposure control */
+ case V4L2_CID_EXPOSURE:
+ set_exposure(gspca_dev, ctrl->val);
+ break;
+ /* standalone gain control */
+ case V4L2_CID_GAIN:
+ set_gain(gspca_dev, ctrl->val);
+ break;
+ /* autogain + exposure or gain control cluster */
+ case V4L2_CID_AUTOGAIN:
+ if (sd->sensor == SENSOR_SOI968)
+ set_gain(gspca_dev, sd->gain->val);
+ else
+ set_exposure(gspca_dev, sd->exposure->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ set_quality(gspca_dev, ctrl->val);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops sd_ctrl_ops = {
+ .s_ctrl = sd_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 13);
+
+ sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 127);
+ sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 127);
+ sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HUE, -180, 180, 1, 0);
+ v4l2_ctrl_cluster(4, &sd->brightness);
+
+ sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAMMA, 0, 255, 1, 0x10);
+
+ sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28);
+ sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28);
+ v4l2_ctrl_cluster(2, &sd->blue);
+
+ if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 &&
+ sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 &&
+ sd->sensor != SENSOR_MT9VPRB) {
+ sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_cluster(2, &sd->hflip);
+ }
+
+ if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB &&
+ sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 &&
+ sd->sensor != SENSOR_MT9V111)
+ sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33);
+
+ if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 &&
+ sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) {
+ sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_GAIN, 0, 28, 1, 0);
+ sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (sd->sensor == SENSOR_SOI968)
+ /* this sensor doesn't have the exposure control and
+ autogain is clustered with gain instead. This works
+ because sd->exposure == NULL. */
+ v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false);
+ else
+ /* Otherwise autogain is clustered with exposure. */
+ v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false);
+ }
+
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ return 0;
+}
+
static int sd_init(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1949,7 +1905,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
pr_err("Unsupported sensor\n");
gspca_dev->usb_err = -ENODEV;
}
-
return gspca_dev->usb_err;
}
@@ -2025,8 +1980,8 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
if (intf->num_altsetting != 9) {
pr_warn("sn9c20x camera with unknown number of alt "
- "settings (%d), please report!\n",
- intf->num_altsetting);
+ "settings (%d), please report!\n",
+ intf->num_altsetting);
gspca_dev->alt = intf->num_altsetting;
return 0;
}
@@ -2067,7 +2022,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
jpeg_define(sd->jpeg_hdr, height, width,
0x21);
- jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+ jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual));
if (mode & MODE_RAW)
fmt = 0x2d;
@@ -2104,12 +2059,17 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x1189, scale);
reg_w1(gspca_dev, 0x10e0, fmt);
- set_cmatrix(gspca_dev);
- set_gamma(gspca_dev);
- set_redblue(gspca_dev);
- set_gain(gspca_dev);
- set_exposure(gspca_dev);
- set_hvflip(gspca_dev);
+ set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness),
+ v4l2_ctrl_g_ctrl(sd->contrast),
+ v4l2_ctrl_g_ctrl(sd->saturation),
+ v4l2_ctrl_g_ctrl(sd->hue));
+ set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma));
+ set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue),
+ v4l2_ctrl_g_ctrl(sd->red));
+ set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain));
+ set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
+ set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip),
+ v4l2_ctrl_g_ctrl(sd->vflip));
reg_w1(gspca_dev, 0x1007, 0x20);
reg_w1(gspca_dev, 0x1061, 0x03);
@@ -2148,6 +2108,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
{
struct sd *sd = (struct sd *) gspca_dev;
+ s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure);
+ s32 max = sd->exposure->maximum - sd->exposure_step;
+ s32 min = sd->exposure->minimum + sd->exposure_step;
s16 new_exp;
/*
@@ -2156,16 +2119,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
* and exposure steps
*/
if (avg_lum < MIN_AVG_LUM) {
- if (sd->ctrls[EXPOSURE].val > 0x1770)
+ if (cur_exp > max)
return;
- new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step;
- if (new_exp > 0x1770)
- new_exp = 0x1770;
- if (new_exp < 0x10)
- new_exp = 0x10;
- sd->ctrls[EXPOSURE].val = new_exp;
- set_exposure(gspca_dev);
+ new_exp = cur_exp + sd->exposure_step;
+ if (new_exp > max)
+ new_exp = max;
+ if (new_exp < min)
+ new_exp = min;
+ v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
sd->older_step = sd->old_step;
sd->old_step = 1;
@@ -2176,15 +2138,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
sd->exposure_step += 2;
}
if (avg_lum > MAX_AVG_LUM) {
- if (sd->ctrls[EXPOSURE].val < 0x10)
+ if (cur_exp < min)
return;
- new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step;
- if (new_exp > 0x1700)
- new_exp = 0x1770;
- if (new_exp < 0x10)
- new_exp = 0x10;
- sd->ctrls[EXPOSURE].val = new_exp;
- set_exposure(gspca_dev);
+ new_exp = cur_exp - sd->exposure_step;
+ if (new_exp > max)
+ new_exp = max;
+ if (new_exp < min)
+ new_exp = min;
+ v4l2_ctrl_s_ctrl(sd->exposure, new_exp);
sd->older_step = sd->old_step;
sd->old_step = 0;
@@ -2198,19 +2159,12 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
{
struct sd *sd = (struct sd *) gspca_dev;
+ s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain);
- if (avg_lum < MIN_AVG_LUM) {
- if (sd->ctrls[GAIN].val + 1 <= 28) {
- sd->ctrls[GAIN].val++;
- set_gain(gspca_dev);
- }
- }
- if (avg_lum > MAX_AVG_LUM) {
- if (sd->ctrls[GAIN].val > 0) {
- sd->ctrls[GAIN].val--;
- set_gain(gspca_dev);
- }
- }
+ if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum)
+ v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1);
+ if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum)
+ v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1);
}
static void sd_dqcallback(struct gspca_dev *gspca_dev)
@@ -2218,7 +2172,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum;
- if (!sd->ctrls[AUTOGAIN].val)
+ if (!v4l2_ctrl_g_ctrl(sd->autogain))
return;
avg_lum = atomic_read(&sd->avg_lum);
@@ -2234,10 +2188,11 @@ static void qual_upd(struct work_struct *work)
{
struct sd *sd = container_of(work, struct sd, work);
struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual);
mutex_lock(&gspca_dev->usb_lock);
- PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val);
- set_quality(gspca_dev);
+ PDEBUG(D_STREAM, "qual_upd %d%%", qual);
+ set_quality(gspca_dev, qual);
mutex_unlock(&gspca_dev->usb_lock);
}
@@ -2286,14 +2241,18 @@ static void transfer_check(struct gspca_dev *gspca_dev,
if (new_qual != 0) {
sd->nchg += new_qual;
if (sd->nchg < -6 || sd->nchg >= 12) {
+ /* Note: we are in interrupt context, so we can't
+ use v4l2_ctrl_g/s_ctrl here. Access the value
+ directly instead. */
+ s32 curqual = sd->jpegqual->cur.val;
sd->nchg = 0;
- new_qual += sd->ctrls[QUALITY].val;
- if (new_qual < QUALITY_MIN)
- new_qual = QUALITY_MIN;
- else if (new_qual > QUALITY_MAX)
- new_qual = QUALITY_MAX;
- if (new_qual != sd->ctrls[QUALITY].val) {
- sd->ctrls[QUALITY].val = new_qual;
+ new_qual += curqual;
+ if (new_qual < sd->jpegqual->minimum)
+ new_qual = sd->jpegqual->minimum;
+ else if (new_qual > sd->jpegqual->maximum)
+ new_qual = sd->jpegqual->maximum;
+ if (new_qual != curqual) {
+ sd->jpegqual->cur.val = new_qual;
queue_work(sd->work_thread, &sd->work);
}
}
@@ -2309,7 +2268,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum, is_jpeg;
- static u8 frame_header[] =
+ static const u8 frame_header[] =
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
is_jpeg = (sd->fmt & 0x03) == 0;
@@ -2373,10 +2332,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description */
static const struct sd_desc sd_desc = {
.name = KBUILD_MODNAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
.isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
index 6a1148d7fe9..e2bdf8f632f 100644
--- a/drivers/media/video/gspca/sonixb.c
+++ b/drivers/media/video/gspca/sonixb.c
@@ -1000,6 +1000,8 @@ static void setfreq(struct gspca_dev *gspca_dev)
}
}
+#define WANT_REGULAR_AUTOGAIN
+#define WANT_COARSE_EXPO_AUTOGAIN
#include "autogain_functions.h"
static void do_autogain(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 863c755dd2b..4d1696d1a7f 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -2800,10 +2800,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
}
-/* !! coarse_grained_expo_autogain is not used !! */
-#define exp_too_low_cnt bridge
-#define exp_too_high_cnt sensor
-
+#define WANT_REGULAR_AUTOGAIN
#include "autogain_functions.h"
static void do_autogain(struct gspca_dev *gspca_dev)
diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
index 2fe3c29bd6b..04f54654a02 100644
--- a/drivers/media/video/gspca/sq905.c
+++ b/drivers/media/video/gspca/sq905.c
@@ -232,7 +232,11 @@ static void sq905_dostream(struct work_struct *work)
frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage
+ FRAME_HEADER_LEN;
- while (gspca_dev->present && gspca_dev->streaming) {
+ while (gspca_dev->dev && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
/* request some data and then read it until we have
* a complete frame. */
bytes_left = frame_sz;
@@ -242,7 +246,7 @@ static void sq905_dostream(struct work_struct *work)
we must finish reading an entire frame, otherwise the
next time we stream we start reading in the middle of a
frame. */
- while (bytes_left > 0 && gspca_dev->present) {
+ while (bytes_left > 0 && gspca_dev->dev) {
data_len = bytes_left > SQ905_MAX_TRANSFER ?
SQ905_MAX_TRANSFER : bytes_left;
ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
@@ -274,7 +278,7 @@ static void sq905_dostream(struct work_struct *work)
gspca_frame_add(gspca_dev, LAST_PACKET,
NULL, 0);
}
- if (gspca_dev->present) {
+ if (gspca_dev->dev) {
/* acknowledge the frame */
mutex_lock(&gspca_dev->usb_lock);
ret = sq905_ack_frame(gspca_dev);
@@ -284,7 +288,7 @@ static void sq905_dostream(struct work_struct *work)
}
}
quit_stream:
- if (gspca_dev->present) {
+ if (gspca_dev->dev) {
mutex_lock(&gspca_dev->usb_lock);
sq905_command(gspca_dev, SQ905_CLEAR);
mutex_unlock(&gspca_dev->usb_lock);
diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
index ae783634712..f34ddb0570c 100644
--- a/drivers/media/video/gspca/sq905c.c
+++ b/drivers/media/video/gspca/sq905c.c
@@ -150,7 +150,11 @@ static void sq905c_dostream(struct work_struct *work)
goto quit_stream;
}
- while (gspca_dev->present && gspca_dev->streaming) {
+ while (gspca_dev->dev && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
/* Request the header, which tells the size to download */
ret = usb_bulk_msg(gspca_dev->dev,
usb_rcvbulkpipe(gspca_dev->dev, 0x81),
@@ -169,7 +173,7 @@ static void sq905c_dostream(struct work_struct *work)
packet_type = FIRST_PACKET;
gspca_frame_add(gspca_dev, packet_type,
buffer, FRAME_HEADER_LEN);
- while (bytes_left > 0 && gspca_dev->present) {
+ while (bytes_left > 0 && gspca_dev->dev) {
data_len = bytes_left > SQ905C_MAX_TRANSFER ?
SQ905C_MAX_TRANSFER : bytes_left;
ret = usb_bulk_msg(gspca_dev->dev,
@@ -191,7 +195,7 @@ static void sq905c_dostream(struct work_struct *work)
}
}
quit_stream:
- if (gspca_dev->present) {
+ if (gspca_dev->dev) {
mutex_lock(&gspca_dev->usb_lock);
sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
mutex_unlock(&gspca_dev->usb_lock);
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
index 91d99b4cc57..999ec776444 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -261,6 +261,17 @@ static int stv06xx_init(struct gspca_dev *gspca_dev)
return (err < 0) ? err : 0;
}
+/* this function is called at probe time */
+static int stv06xx_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_PROBE, "Initializing controls");
+
+ gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler;
+ return sd->sensor->init_controls(sd);
+}
+
/* Start the camera */
static int stv06xx_start(struct gspca_dev *gspca_dev)
{
@@ -512,6 +523,7 @@ static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.config = stv06xx_config,
.init = stv06xx_init,
+ .init_controls = stv06xx_init_controls,
.start = stv06xx_start,
.stopN = stv06xx_stopN,
.pkt_scan = stv06xx_pkt_scan,
@@ -530,9 +542,8 @@ static int stv06xx_config(struct gspca_dev *gspca_dev,
PDEBUG(D_PROBE, "Configuring camera");
- sd->desc = sd_desc;
sd->bridge = id->driver_info;
- gspca_dev->sd_desc = &sd->desc;
+ gspca_dev->sd_desc = &sd_desc;
if (dump_bridge)
stv06xx_dump_bridge(sd);
@@ -594,11 +605,12 @@ static void sd_disconnect(struct usb_interface *intf)
{
struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
struct sd *sd = (struct sd *) gspca_dev;
+ void *priv = sd->sensor_priv;
PDEBUG(D_PROBE, "Disconnecting the stv06xx device");
- if (sd->sensor->disconnect)
- sd->sensor->disconnect(sd);
+ sd->sensor = NULL;
gspca_disconnect(intf);
+ kfree(priv);
}
static struct usb_driver sd_driver = {
@@ -609,6 +621,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h
index d270a5981af..34957a4ec15 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx.h
@@ -89,9 +89,6 @@ struct sd {
/* 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;
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
index a8698b7a756..06fa54c5efb 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
@@ -32,36 +32,6 @@
#include "stv06xx_hdcs.h"
-static const struct ctrl hdcs1x00_ctrl[] = {
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "exposure",
- .minimum = 0x00,
- .maximum = 0xff,
- .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
- }
-};
-
static struct v4l2_pix_format hdcs1x00_mode[] = {
{
HDCS_1X00_DEF_WIDTH,
@@ -76,36 +46,6 @@ static struct v4l2_pix_format hdcs1x00_mode[] = {
}
};
-static const struct ctrl hdcs1020_ctrl[] = {
- {
- {
- .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
- }
-};
-
static struct v4l2_pix_format hdcs1020_mode[] = {
{
HDCS_1020_DEF_WIDTH,
@@ -150,7 +90,6 @@ struct hdcs {
} exp;
int psmp;
- u8 exp_cache, gain_cache;
};
static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len)
@@ -232,16 +171,6 @@ static int hdcs_reset(struct sd *sd)
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;
-
- *val = hdcs->exp_cache;
-
- return 0;
-}
-
static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -260,9 +189,6 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
int cycles, err;
u8 exp[14];
- val &= 0xff;
- hdcs->exp_cache = val;
-
cycles = val * HDCS_CLK_FREQ_MHZ * 257;
ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2);
@@ -336,12 +262,9 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
static int hdcs_set_gains(struct sd *sd, u8 g)
{
- struct hdcs *hdcs = sd->sensor_priv;
int err;
u8 gains[4];
- hdcs->gain_cache = g;
-
/* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */
if (g > 127)
g = 0x80 | (g / 2);
@@ -352,17 +275,7 @@ static int hdcs_set_gains(struct sd *sd, u8 g)
gains[3] = g;
err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4);
- return err;
-}
-
-static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- struct hdcs *hdcs = sd->sensor_priv;
-
- *val = hdcs->gain_cache;
-
- return 0;
+ return err;
}
static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val)
@@ -420,6 +333,39 @@ static int hdcs_set_size(struct sd *sd,
return err;
}
+static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ int err = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ err = hdcs_set_gain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ err = hdcs_set_exposure(gspca_dev, ctrl->val);
+ break;
+ }
+ return err;
+}
+
+static const struct v4l2_ctrl_ops hdcs_ctrl_ops = {
+ .s_ctrl = hdcs_s_ctrl,
+};
+
+static int hdcs_init_controls(struct sd *sd)
+{
+ struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+ v4l2_ctrl_handler_init(hdl, 2);
+ v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE);
+ v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops,
+ V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN);
+ return hdl->error;
+}
+
static int hdcs_probe_1x00(struct sd *sd)
{
struct hdcs *hdcs;
@@ -434,8 +380,6 @@ static int hdcs_probe_1x00(struct sd *sd)
sd->gspca_dev.cam.cam_mode = hdcs1x00_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode);
- sd->desc.ctrls = hdcs1x00_ctrl;
- sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl);
hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
if (!hdcs)
@@ -493,8 +437,6 @@ static int hdcs_probe_1020(struct sd *sd)
sd->gspca_dev.cam.cam_mode = hdcs1020_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode);
- sd->desc.ctrls = hdcs1020_ctrl;
- sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl);
hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
if (!hdcs)
@@ -537,12 +479,6 @@ static int hdcs_stop(struct sd *sd)
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;
@@ -587,16 +523,7 @@ static int hdcs_init(struct sd *sd)
if (err < 0)
return err;
- err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN);
- if (err < 0)
- return err;
-
- err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
- if (err < 0)
- return err;
-
- err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE);
- return err;
+ return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height);
}
static int hdcs_dump(struct sd *sd)
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
index a14a84a5079..1ba9158d010 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
@@ -131,14 +131,12 @@ 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_init_controls(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",
@@ -152,10 +150,10 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
.max_packet_size = { 847 },
.init = hdcs_init,
+ .init_controls = hdcs_init_controls,
.probe = hdcs_probe_1x00,
.start = hdcs_start,
.stop = hdcs_stop,
- .disconnect = hdcs_disconnect,
.dump = hdcs_dump,
};
@@ -171,6 +169,7 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
.max_packet_size = { 847 },
.init = hdcs_init,
+ .init_controls = hdcs_init_controls,
.probe = hdcs_probe_1020,
.start = hdcs_start,
.stop = hdcs_stop,
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
index 26f14fc4a13..cdfc3d05ab6 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
@@ -48,105 +48,16 @@
#include "stv06xx_pb0100.h"
-static const struct ctrl pb0100_ctrl[] = {
-#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
- }
+struct pb0100_ctrls {
+ struct { /* one big happy control cluster... */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *gain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *red;
+ struct v4l2_ctrl *blue;
+ struct v4l2_ctrl *natural;
+ };
+ struct v4l2_ctrl *target;
};
static struct v4l2_pix_format pb0100_mode[] = {
@@ -174,38 +85,104 @@ static struct v4l2_pix_format pb0100_mode[] = {
}
};
+static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct pb0100_ctrls *ctrls = sd->sensor_priv;
+ int err = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ err = pb0100_set_autogain(gspca_dev, ctrl->val);
+ if (err)
+ break;
+ if (ctrl->val)
+ break;
+ err = pb0100_set_gain(gspca_dev, ctrls->gain->val);
+ if (err)
+ break;
+ err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val);
+ break;
+ case V4L2_CTRL_CLASS_USER + 0x1001:
+ err = pb0100_set_autogain_target(gspca_dev, ctrl->val);
+ break;
+ }
+ return err;
+}
+
+static const struct v4l2_ctrl_ops pb0100_ctrl_ops = {
+ .s_ctrl = pb0100_s_ctrl,
+};
+
+static int pb0100_init_controls(struct sd *sd)
+{
+ struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+ struct pb0100_ctrls *ctrls;
+ static const struct v4l2_ctrl_config autogain_target = {
+ .ops = &pb0100_ctrl_ops,
+ .id = V4L2_CTRL_CLASS_USER + 0x1000,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Automatic Gain Target",
+ .max = 255,
+ .step = 1,
+ .def = 128,
+ };
+ static const struct v4l2_ctrl_config natural_light = {
+ .ops = &pb0100_ctrl_ops,
+ .id = V4L2_CTRL_CLASS_USER + 0x1001,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Natural Light Source",
+ .max = 1,
+ .step = 1,
+ .def = 1,
+ };
+
+ ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL);
+ if (!ctrls)
+ return -ENOMEM;
+
+ v4l2_ctrl_handler_init(hdl, 6);
+ ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 511, 1, 12);
+ ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 128);
+ ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+ V4L2_CID_RED_BALANCE, -255, 255, 1, 0);
+ ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops,
+ V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0);
+ ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL);
+ ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL);
+ if (hdl->error) {
+ kfree(ctrls);
+ return hdl->error;
+ }
+ sd->sensor_priv = ctrls;
+ v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false);
+ return 0;
+}
+
static int pb0100_probe(struct sd *sd)
{
u16 sensor;
- int i, err;
- s32 *sensor_settings;
+ int err;
err = stv06xx_read_sensor(sd, PB_IDENT, &sensor);
if (err < 0)
return -ENODEV;
+ if ((sensor >> 8) != 0x64)
+ return -ENODEV;
- if ((sensor >> 8) == 0x64) {
- sensor_settings = kmalloc(
- ARRAY_SIZE(pb0100_ctrl) * sizeof(s32),
- GFP_KERNEL);
- if (!sensor_settings)
- return -ENOMEM;
-
- pr_info("Photobit pb0100 sensor detected\n");
-
- sd->gspca_dev.cam.cam_mode = pb0100_mode;
- sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
- sd->desc.ctrls = pb0100_ctrl;
- sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl);
- for (i = 0; i < sd->desc.nctrls; i++)
- sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value;
- sd->sensor_priv = sensor_settings;
+ pr_info("Photobit pb0100 sensor detected\n");
- return 0;
- }
+ sd->gspca_dev.cam.cam_mode = pb0100_mode;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
- return -ENODEV;
+ return 0;
}
static int pb0100_start(struct sd *sd)
@@ -214,7 +191,6 @@ static int pb0100_start(struct sd *sd)
struct usb_host_interface *alt;
struct usb_interface *intf;
struct cam *cam = &sd->gspca_dev.cam;
- s32 *sensor_settings = sd->sensor_priv;
u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
@@ -255,13 +231,6 @@ static int pb0100_start(struct sd *sd)
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);
@@ -285,12 +254,6 @@ out:
return (err < 0) ? err : 0;
}
-static void pb0100_disconnect(struct sd *sd)
-{
- sd->sensor = NULL;
- kfree(sd->sensor_priv);
-}
-
/* 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,
@@ -362,62 +325,32 @@ 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;
+ struct pb0100_ctrls *ctrls = 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]);
+ err = pb0100_set_red_balance(gspca_dev, ctrls->red->val);
if (!err)
- err = pb0100_set_blue_balance(gspca_dev,
- sensor_settings[BLUE_BALANCE_IDX]);
+ err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val);
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;
+ struct pb0100_ctrls *ctrls = sd->sensor_priv;
- if (sensor_settings[AUTOGAIN_IDX])
- return -EBUSY;
-
- sensor_settings[RED_BALANCE_IDX] = val;
- val += sensor_settings[GAIN_IDX];
+ val += ctrls->gain->val;
if (val < 0)
val = 0;
else if (val > 255)
@@ -429,27 +362,13 @@ static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val)
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;
+ struct pb0100_ctrls *ctrls = sd->sensor_priv;
- if (sensor_settings[AUTOGAIN_IDX])
- return -EBUSY;
-
- sensor_settings[BLUE_BALANCE_IDX] = val;
- val += sensor_settings[GAIN_IDX];
+ val += ctrls->gain->val;
if (val < 0)
val = 0;
else if (val > 255)
@@ -461,51 +380,25 @@ static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val)
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;
+ int err;
- 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;
+ struct pb0100_ctrls *ctrls = sd->sensor_priv;
- sensor_settings[AUTOGAIN_IDX] = val;
- if (sensor_settings[AUTOGAIN_IDX]) {
- if (sensor_settings[NATURAL_IDX])
+ if (val) {
+ if (ctrls->natural->val)
val = BIT(6)|BIT(4)|BIT(0);
else
val = BIT(4)|BIT(0);
@@ -514,29 +407,15 @@ static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val)
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);
+ val, ctrls->natural->val, 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 */
@@ -553,23 +432,3 @@ static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val)
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
index 757de246dc7..5071e5353fd 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
@@ -112,25 +112,17 @@
static int pb0100_probe(struct sd *sd);
static int pb0100_start(struct sd *sd);
static int pb0100_init(struct sd *sd);
+static int pb0100_init_controls(struct sd *sd);
static int pb0100_stop(struct sd *sd);
static int pb0100_dump(struct sd *sd);
-static void pb0100_disconnect(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",
@@ -142,11 +134,11 @@ const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
.max_packet_size = { 847, 923 },
.init = pb0100_init,
+ .init_controls = pb0100_init_controls,
.probe = pb0100_probe,
.start = pb0100_start,
.stop = pb0100_stop,
.dump = pb0100_dump,
- .disconnect = pb0100_disconnect,
};
#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
index fb229d8ded5..3a498c2495c 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
@@ -63,8 +63,8 @@ struct stv06xx_sensor {
/* Performs a initialization sequence */
int (*init)(struct sd *sd);
- /* Executed at device disconnect */
- void (*disconnect)(struct sd *sd);
+ /* Initializes the controls */
+ int (*init_controls)(struct sd *sd);
/* Reads a sensor register */
int (*read_sensor)(struct sd *sd, const u8 address,
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
index 9940e035b3a..8a57990dfe0 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
@@ -30,20 +30,6 @@
#include "stv06xx_st6422.h"
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- GAIN,
- EXPOSURE,
- NCTRLS /* number of controls */
-};
-
-/* sensor settings */
-struct st6422_settings {
- struct gspca_ctrl ctrls[NCTRLS];
-};
-
static struct v4l2_pix_format st6422_mode[] = {
/* Note we actually get 124 lines of data, of which we skip the 4st
4 as they are garbage */
@@ -74,83 +60,70 @@ static struct v4l2_pix_format st6422_mode[] = {
};
/* V4L2 controls supported by the driver */
-static void st6422_set_brightness(struct gspca_dev *gspca_dev);
-static void st6422_set_contrast(struct gspca_dev *gspca_dev);
-static void st6422_set_gain(struct gspca_dev *gspca_dev);
-static void st6422_set_exposure(struct gspca_dev *gspca_dev);
-
-static const struct ctrl st6422_ctrl[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 31,
- .step = 1,
- .default_value = 3
- },
- .set_control = st6422_set_brightness
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = 11
- },
- .set_control = st6422_set_contrast
- },
-[GAIN] = {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 64
- },
- .set_control = st6422_set_gain
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0,
-#define EXPOSURE_MAX 1023
- .maximum = EXPOSURE_MAX,
- .step = 1,
- .default_value = 256
- },
- .set_control = st6422_set_exposure
- },
+static int setbrightness(struct sd *sd, s32 val);
+static int setcontrast(struct sd *sd, s32 val);
+static int setgain(struct sd *sd, u8 gain);
+static int setexposure(struct sd *sd, s16 expo);
+
+static int st6422_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+ int err = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ err = setbrightness(sd, ctrl->val);
+ break;
+ case V4L2_CID_CONTRAST:
+ err = setcontrast(sd, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ err = setgain(sd, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ err = setexposure(sd, ctrl->val);
+ break;
+ }
+
+ /* commit settings */
+ if (err >= 0)
+ err = stv06xx_write_bridge(sd, 0x143f, 0x01);
+ sd->gspca_dev.usb_err = err;
+ return err;
+}
+
+static const struct v4l2_ctrl_ops st6422_ctrl_ops = {
+ .s_ctrl = st6422_s_ctrl,
};
-static int st6422_probe(struct sd *sd)
+static int st6422_init_controls(struct sd *sd)
{
- struct st6422_settings *sensor_settings;
+ struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 31, 1, 3);
+ v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 15, 1, 11);
+ v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 1023, 1, 256);
+ v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops,
+ V4L2_CID_GAIN, 0, 255, 1, 64);
+
+ return hdl->error;
+}
+static int st6422_probe(struct sd *sd)
+{
if (sd->bridge != BRIDGE_ST6422)
return -ENODEV;
pr_info("st6422 sensor detected\n");
- sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL);
- if (!sensor_settings)
- return -ENOMEM;
-
sd->gspca_dev.cam.cam_mode = st6422_mode;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode);
- sd->gspca_dev.cam.ctrls = sensor_settings->ctrls;
- sd->desc.ctrls = st6422_ctrl;
- sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl);
- sd->sensor_priv = sensor_settings;
-
return 0;
}
@@ -239,38 +212,22 @@ static int st6422_init(struct sd *sd)
return err;
}
-static void st6422_disconnect(struct sd *sd)
-{
- sd->sensor = NULL;
- kfree(sd->sensor_priv);
-}
-
-static int setbrightness(struct sd *sd)
+static int setbrightness(struct sd *sd, s32 val)
{
- struct st6422_settings *sensor_settings = sd->sensor_priv;
-
/* val goes from 0 -> 31 */
- return stv06xx_write_bridge(sd, 0x1432,
- sensor_settings->ctrls[BRIGHTNESS].val);
+ return stv06xx_write_bridge(sd, 0x1432, val);
}
-static int setcontrast(struct sd *sd)
+static int setcontrast(struct sd *sd, s32 val)
{
- struct st6422_settings *sensor_settings = sd->sensor_priv;
-
/* Val goes from 0 -> 15 */
- return stv06xx_write_bridge(sd, 0x143a,
- sensor_settings->ctrls[CONTRAST].val | 0xf0);
+ return stv06xx_write_bridge(sd, 0x143a, val | 0xf0);
}
-static int setgain(struct sd *sd)
+static int setgain(struct sd *sd, u8 gain)
{
- struct st6422_settings *sensor_settings = sd->sensor_priv;
- u8 gain;
int err;
- gain = sensor_settings->ctrls[GAIN].val;
-
/* Set red, green, blue, gain */
err = stv06xx_write_bridge(sd, 0x0509, gain);
if (err < 0)
@@ -292,13 +249,10 @@ static int setgain(struct sd *sd)
return stv06xx_write_bridge(sd, 0x050d, 0x01);
}
-static int setexposure(struct sd *sd)
+static int setexposure(struct sd *sd, s16 expo)
{
- struct st6422_settings *sensor_settings = sd->sensor_priv;
- u16 expo;
int err;
- expo = sensor_settings->ctrls[EXPOSURE].val;
err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff);
if (err < 0)
return err;
@@ -318,22 +272,6 @@ static int st6422_start(struct sd *sd)
if (err < 0)
return err;
- err = setbrightness(sd);
- if (err < 0)
- return err;
-
- err = setcontrast(sd);
- if (err < 0)
- return err;
-
- err = setexposure(sd);
- if (err < 0)
- return err;
-
- err = setgain(sd);
- if (err < 0)
- return err;
-
/* commit settings */
err = stv06xx_write_bridge(sd, 0x143f, 0x01);
return (err < 0) ? err : 0;
@@ -345,59 +283,3 @@ static int st6422_stop(struct sd *sd)
return 0;
}
-
-static void st6422_set_brightness(struct gspca_dev *gspca_dev)
-{
- int err;
- struct sd *sd = (struct sd *) gspca_dev;
-
- err = setbrightness(sd);
-
- /* commit settings */
- if (err >= 0)
- err = stv06xx_write_bridge(sd, 0x143f, 0x01);
-
- gspca_dev->usb_err = err;
-}
-
-static void st6422_set_contrast(struct gspca_dev *gspca_dev)
-{
- int err;
- struct sd *sd = (struct sd *) gspca_dev;
-
- err = setcontrast(sd);
-
- /* commit settings */
- if (err >= 0)
- err = stv06xx_write_bridge(sd, 0x143f, 0x01);
-
- gspca_dev->usb_err = err;
-}
-
-static void st6422_set_gain(struct gspca_dev *gspca_dev)
-{
- int err;
- struct sd *sd = (struct sd *) gspca_dev;
-
- err = setgain(sd);
-
- /* commit settings */
- if (err >= 0)
- err = stv06xx_write_bridge(sd, 0x143f, 0x01);
-
- gspca_dev->usb_err = err;
-}
-
-static void st6422_set_exposure(struct gspca_dev *gspca_dev)
-{
- int err;
- struct sd *sd = (struct sd *) gspca_dev;
-
- err = setexposure(sd);
-
- /* commit settings */
- if (err >= 0)
- err = stv06xx_write_bridge(sd, 0x143f, 0x01);
-
- gspca_dev->usb_err = err;
-}
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h
index d7498e06432..8f20fbf30f3 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h
@@ -34,8 +34,8 @@
static int st6422_probe(struct sd *sd);
static int st6422_start(struct sd *sd);
static int st6422_init(struct sd *sd);
+static int st6422_init_controls(struct sd *sd);
static int st6422_stop(struct sd *sd);
-static void st6422_disconnect(struct sd *sd);
const struct stv06xx_sensor stv06xx_sensor_st6422 = {
.name = "ST6422",
@@ -43,10 +43,10 @@ const struct stv06xx_sensor stv06xx_sensor_st6422 = {
.min_packet_size = { 300, 847 },
.max_packet_size = { 300, 847 },
.init = st6422_init,
+ .init_controls = st6422_init_controls,
.probe = st6422_probe,
.start = st6422_start,
.stop = st6422_stop,
- .disconnect = st6422_disconnect,
};
#endif
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
index a5c69d9ebdd..748e1421d6d 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
@@ -44,130 +44,83 @@ static struct v4l2_pix_format vv6410_mode[] = {
}
};
-static const struct ctrl vv6410_ctrl[] = {
-#define HFLIP_IDX 0
- {
- {
- .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
- },
-#define VFLIP_IDX 1
- {
- {
- .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
- },
-#define GAIN_IDX 2
- {
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "analog gain",
- .minimum = 0,
- .maximum = 15,
- .step = 1,
- .default_value = 10
- },
- .set = vv6410_set_analog_gain,
- .get = vv6410_get_analog_gain
- },
-#define EXPOSURE_IDX 3
- {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "exposure",
- .minimum = 0,
- .maximum = 32768,
- .step = 1,
- .default_value = 20000
- },
- .set = vv6410_set_exposure,
- .get = vv6410_get_exposure
+static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ int err = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_HFLIP:
+ err = vv6410_set_hflip(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ err = vv6410_set_vflip(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_GAIN:
+ err = vv6410_set_analog_gain(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ err = vv6410_set_exposure(gspca_dev, ctrl->val);
+ break;
}
- };
+ return err;
+}
+
+static const struct v4l2_ctrl_ops vv6410_ctrl_ops = {
+ .s_ctrl = vv6410_s_ctrl,
+};
static int vv6410_probe(struct sd *sd)
{
u16 data;
- int err, i;
- s32 *sensor_settings;
+ int err;
err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
if (err < 0)
return -ENODEV;
- if (data == 0x19) {
- pr_info("vv6410 sensor detected\n");
+ if (data != 0x19)
+ return -ENODEV;
- sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32),
- GFP_KERNEL);
- if (!sensor_settings)
- return -ENOMEM;
+ pr_info("vv6410 sensor detected\n");
- sd->gspca_dev.cam.cam_mode = vv6410_mode;
- sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
- sd->desc.ctrls = vv6410_ctrl;
- sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl);
+ sd->gspca_dev.cam.cam_mode = vv6410_mode;
+ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
+ return 0;
+}
- for (i = 0; i < sd->desc.nctrls; i++)
- sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value;
- sd->sensor_priv = sensor_settings;
- return 0;
- }
- return -ENODEV;
+static int vv6410_init_controls(struct sd *sd)
+{
+ struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler;
+
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 32768, 1, 20000);
+ v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops,
+ V4L2_CID_GAIN, 0, 15, 1, 10);
+ return hdl->error;
}
static int vv6410_init(struct sd *sd)
{
int err = 0, i;
- s32 *sensor_settings = sd->sensor_priv;
- for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) {
+ for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++)
stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data);
- }
if (err < 0)
return err;
err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init,
ARRAY_SIZE(vv6410_sensor_init));
- if (err < 0)
- return err;
-
- err = vv6410_set_exposure(&sd->gspca_dev,
- sensor_settings[EXPOSURE_IDX]);
- if (err < 0)
- return err;
-
- err = vv6410_set_analog_gain(&sd->gspca_dev,
- sensor_settings[GAIN_IDX]);
-
return (err < 0) ? err : 0;
}
-static void vv6410_disconnect(struct sd *sd)
-{
- sd->sensor = NULL;
- kfree(sd->sensor_priv);
-}
-
static int vv6410_start(struct sd *sd)
{
int err;
@@ -233,25 +186,12 @@ static int vv6410_dump(struct sd *sd)
return (err < 0) ? err : 0;
}
-static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- s32 *sensor_settings = sd->sensor_priv;
-
- *val = sensor_settings[HFLIP_IDX];
- PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
-
- return 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;
- s32 *sensor_settings = sd->sensor_priv;
- sensor_settings[HFLIP_IDX] = val;
err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
if (err < 0)
return err;
@@ -267,25 +207,12 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
return (err < 0) ? err : 0;
}
-static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- s32 *sensor_settings = sd->sensor_priv;
-
- *val = sensor_settings[VFLIP_IDX];
- PDEBUG(D_V4L2, "Read vertical flip %d", *val);
-
- return 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;
- s32 *sensor_settings = sd->sensor_priv;
- sensor_settings[VFLIP_IDX] = val;
err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
if (err < 0)
return err;
@@ -301,52 +228,23 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
return (err < 0) ? err : 0;
}
-static int vv6410_get_analog_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];
-
- PDEBUG(D_V4L2, "Read analog gain %d", *val);
-
- return 0;
-}
-
static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
- s32 *sensor_settings = sd->sensor_priv;
- sensor_settings[GAIN_IDX] = val;
PDEBUG(D_V4L2, "Set analog gain to %d", val);
err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
return (err < 0) ? err : 0;
}
-static int vv6410_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];
-
- PDEBUG(D_V4L2, "Read exposure %d", *val);
-
- return 0;
-}
-
static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val)
{
int err;
struct sd *sd = (struct sd *) gspca_dev;
- s32 *sensor_settings = sd->sensor_priv;
unsigned int fine, coarse;
- sensor_settings[EXPOSURE_IDX] = val;
-
val = (val * val >> 14) + val / 4;
fine = val % VV6410_CIF_LINELENGTH;
diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
index a25b8873f2e..53e67b40ca0 100644
--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
@@ -178,18 +178,14 @@
static int vv6410_probe(struct sd *sd);
static int vv6410_start(struct sd *sd);
static int vv6410_init(struct sd *sd);
+static int vv6410_init_controls(struct sd *sd);
static int vv6410_stop(struct sd *sd);
static int vv6410_dump(struct sd *sd);
-static void vv6410_disconnect(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);
-static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val);
static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
@@ -202,11 +198,11 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
.min_packet_size = { 1023 },
.max_packet_size = { 1023 },
.init = vv6410_init,
+ .init_controls = vv6410_init_controls,
.probe = vv6410_probe,
.start = vv6410_start,
.stop = vv6410_stop,
.dump = vv6410_dump,
- .disconnect = vv6410_disconnect,
};
/* If NULL, only single value to write, stored in len */
diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c
index 444d3c5b907..c6326d177a3 100644
--- a/drivers/media/video/gspca/topro.c
+++ b/drivers/media/video/gspca/topro.c
@@ -4675,11 +4675,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
/* -- do autogain -- */
/* gain setting is done in setexposure() for tp6810 */
static void setgain(struct gspca_dev *gspca_dev) {}
-/* !! coarse_grained_expo_autogain is not used !! */
-#define exp_too_low_cnt bridge
-#define exp_too_high_cnt sensor
-
+#define WANT_REGULAR_AUTOGAIN
#include "autogain_functions.h"
+
static void sd_dq_callback(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c
index 911152e169d..15a30f7a4b2 100644
--- a/drivers/media/video/gspca/vicam.c
+++ b/drivers/media/video/gspca/vicam.c
@@ -37,9 +37,12 @@
#include <linux/ihex.h>
#include "gspca.h"
+#define VICAM_FIRMWARE "vicam/firmware.fw"
+
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(VICAM_FIRMWARE);
enum e_ctrl {
GAIN,
@@ -222,7 +225,11 @@ static void vicam_dostream(struct work_struct *work)
goto exit;
}
- while (gspca_dev->present && gspca_dev->streaming) {
+ while (gspca_dev->dev && gspca_dev->streaming) {
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
+ break;
+#endif
ret = vicam_read_frame(gspca_dev, buffer, frame_sz);
if (ret < 0)
break;
@@ -268,7 +275,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
const struct firmware *uninitialized_var(fw);
u8 *firmware_buf;
- ret = request_ihex_firmware(&fw, "vicam/firmware.fw",
+ ret = request_ihex_firmware(&fw, VICAM_FIRMWARE,
&gspca_dev->dev->dev);
if (ret) {
pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret);
@@ -324,7 +331,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
dev->work_thread = NULL;
mutex_lock(&gspca_dev->usb_lock);
- if (gspca_dev->present)
+ if (gspca_dev->dev)
vicam_set_camera_power(gspca_dev, 0);
}
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index 7d9a4f1be9d..f0bacee33ef 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -32,29 +32,25 @@ MODULE_LICENSE("GPL");
static int force_sensor = -1;
-#define REG08_DEF 3 /* default JPEG compression (70%) */
+#define REG08_DEF 3 /* default JPEG compression (75%) */
#include "zc3xx-reg.h"
-/* controls */
-enum e_ctrl {
- BRIGHTNESS,
- CONTRAST,
- EXPOSURE,
- GAMMA,
- AUTOGAIN,
- LIGHTFREQ,
- SHARPNESS,
- QUALITY,
- NCTRLS /* number of controls */
-};
-
-#define AUTOGAIN_DEF 1
-
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- struct gspca_ctrl ctrls[NCTRLS];
+ struct { /* gamma/brightness/contrast control cluster */
+ struct v4l2_ctrl *gamma;
+ struct v4l2_ctrl *brightness;
+ struct v4l2_ctrl *contrast;
+ };
+ struct { /* autogain/exposure control cluster */
+ struct v4l2_ctrl *autogain;
+ struct v4l2_ctrl *exposure;
+ };
+ struct v4l2_ctrl *plfreq;
+ struct v4l2_ctrl *sharpness;
+ struct v4l2_ctrl *jpegqual;
struct work_struct work;
struct workqueue_struct *work_thread;
@@ -94,114 +90,6 @@ enum sensors {
SENSOR_MAX
};
-/* V4L2 controls supported by the driver */
-static void setcontrast(struct gspca_dev *gspca_dev);
-static void setexposure(struct gspca_dev *gspca_dev);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static void setlightfreq(struct gspca_dev *gspca_dev);
-static void setsharpness(struct gspca_dev *gspca_dev);
-static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val);
-
-static const struct ctrl sd_ctrls[NCTRLS] = {
-[BRIGHTNESS] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- },
- .set_control = setcontrast
- },
-[CONTRAST] = {
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 128,
- },
- .set_control = setcontrast
- },
-[EXPOSURE] = {
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Exposure",
- .minimum = 0x30d,
- .maximum = 0x493e,
- .step = 1,
- .default_value = 0x927
- },
- .set_control = setexposure
- },
-[GAMMA] = {
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 1,
- .maximum = 6,
- .step = 1,
- .default_value = 4,
- },
- .set_control = setcontrast
- },
-[AUTOGAIN] = {
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = AUTOGAIN_DEF,
- .flags = V4L2_CTRL_FLAG_UPDATE
- },
- .set = sd_setautogain
- },
-[LIGHTFREQ] = {
- {
- .id = V4L2_CID_POWER_LINE_FREQUENCY,
- .type = V4L2_CTRL_TYPE_MENU,
- .name = "Light frequency filter",
- .minimum = 0,
- .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
- .step = 1,
- .default_value = 0,
- },
- .set_control = setlightfreq
- },
-[SHARPNESS] = {
- {
- .id = V4L2_CID_SHARPNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Sharpness",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 2,
- },
- .set_control = setsharpness
- },
-[QUALITY] = {
- {
- .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Compression Quality",
- .minimum = 40,
- .maximum = 70,
- .step = 1,
- .default_value = 70 /* updated in sd_init() */
- },
- .set = sd_setquality
- },
-};
-
static const struct v4l2_pix_format vga_mode[] = {
{320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
.bytesperline = 320,
@@ -241,8 +129,11 @@ static const struct v4l2_pix_format sif_mode[] = {
.priv = 0},
};
-/* bridge reg08 -> JPEG quality conversion table */
-static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/};
+/*
+ * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest
+ * quality setting is not usable as USB 1 does not have enough bandwidth.
+ */
+static u8 jpeg_qual[] = {50, 75, 87, /* 94 */};
/* usb exchanges */
struct usb_action {
@@ -5818,10 +5709,8 @@ static void setmatrix(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, matrix[i], 0x010a + i);
}
-static void setsharpness(struct gspca_dev *gspca_dev)
+static void setsharpness(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- int sharpness;
static const u8 sharpness_tb[][2] = {
{0x02, 0x03},
{0x04, 0x07},
@@ -5829,19 +5718,18 @@ static void setsharpness(struct gspca_dev *gspca_dev)
{0x10, 0x1e}
};
- sharpness = sd->ctrls[SHARPNESS].val;
- reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6);
+ reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6);
reg_r(gspca_dev, 0x01c8);
reg_r(gspca_dev, 0x01c9);
reg_r(gspca_dev, 0x01ca);
- reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb);
+ reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb);
}
-static void setcontrast(struct gspca_dev *gspca_dev)
+static void setcontrast(struct gspca_dev *gspca_dev,
+ s32 gamma, s32 brightness, s32 contrast)
{
- struct sd *sd = (struct sd *) gspca_dev;
const u8 *Tgamma;
- int g, i, brightness, contrast, adj, gp1, gp2;
+ int g, i, adj, gp1, gp2;
u8 gr[16];
static const u8 delta_b[16] = /* delta for brightness */
{0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d,
@@ -5864,10 +5752,10 @@ static void setcontrast(struct gspca_dev *gspca_dev)
0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
};
- Tgamma = gamma_tb[sd->ctrls[GAMMA].val - 1];
+ Tgamma = gamma_tb[gamma - 1];
- contrast = ((int) sd->ctrls[CONTRAST].val - 128); /* -128 / 127 */
- brightness = ((int) sd->ctrls[BRIGHTNESS].val - 128); /* -128 / 92 */
+ contrast -= 128; /* -128 / 127 */
+ brightness -= 128; /* -128 / 92 */
adj = 0;
gp1 = gp2 = 0;
for (i = 0; i < 16; i++) {
@@ -5894,25 +5782,15 @@ static void setcontrast(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */
}
-static void getexposure(struct gspca_dev *gspca_dev)
+static s32 getexposure(struct gspca_dev *gspca_dev)
{
- struct sd *sd = (struct sd *) gspca_dev;
-
- if (sd->sensor != SENSOR_HV7131R)
- return;
- sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9)
+ return (i2c_read(gspca_dev, 0x25) << 9)
| (i2c_read(gspca_dev, 0x26) << 1)
| (i2c_read(gspca_dev, 0x27) >> 7);
}
-static void setexposure(struct gspca_dev *gspca_dev)
+static void setexposure(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- int val;
-
- if (sd->sensor != SENSOR_HV7131R)
- return;
- val = sd->ctrls[EXPOSURE].val;
i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
i2c_write(gspca_dev, 0x27, val << 7, 0x00);
@@ -5921,20 +5799,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
static void setquality(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- s8 reg07;
-
- reg07 = 0;
- switch (sd->sensor) {
- case SENSOR_OV7620:
- reg07 = 0x30;
- break;
- case SENSOR_HV7131R:
- case SENSOR_PAS202B:
- return; /* done by work queue */
- }
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]);
reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
- if (reg07 != 0)
- reg_w(gspca_dev, reg07, 0x0007);
}
/* Matches the sensor's internal frame rate to the lighting frequency.
@@ -5943,7 +5809,7 @@ static void setquality(struct gspca_dev *gspca_dev)
* 60Hz, for American lighting
* 0 = No Fliker (for outdoore usage)
*/
-static void setlightfreq(struct gspca_dev *gspca_dev)
+static void setlightfreq(struct gspca_dev *gspca_dev, s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
int i, mode;
@@ -6027,7 +5893,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
tas5130c_60HZ, tas5130c_60HZScale},
};
- i = sd->ctrls[LIGHTFREQ].val * 2;
+ i = val * 2;
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
if (mode)
i++; /* 320x240 */
@@ -6037,14 +5903,14 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
usb_exchange(gspca_dev, zc3_freq);
switch (sd->sensor) {
case SENSOR_GC0305:
- if (mode /* if 320x240 */
- && sd->ctrls[LIGHTFREQ].val == 1) /* and 50Hz */
+ if (mode /* if 320x240 */
+ && val == 1) /* and 50Hz */
reg_w(gspca_dev, 0x85, 0x018d);
/* win: 0x80, 0x018d */
break;
case SENSOR_OV7620:
- if (!mode) { /* if 640x480 */
- if (sd->ctrls[LIGHTFREQ].val != 0) /* and filter */
+ if (!mode) { /* if 640x480 */
+ if (val != 0) /* and filter */
reg_w(gspca_dev, 0x40, 0x0002);
else
reg_w(gspca_dev, 0x44, 0x0002);
@@ -6056,22 +5922,15 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
}
}
-static void setautogain(struct gspca_dev *gspca_dev)
+static void setautogain(struct gspca_dev *gspca_dev, s32 val)
{
- struct sd *sd = (struct sd *) gspca_dev;
- u8 autoval;
-
- if (sd->ctrls[AUTOGAIN].val)
- autoval = 0x42;
- else
- autoval = 0x02;
- reg_w(gspca_dev, autoval, 0x0180);
+ reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180);
}
-/* update the transfer parameters */
-/* This function is executed from a work queue. */
-/* The exact use of the bridge registers 07 and 08 is not known.
- * The following algorithm has been adapted from ms-win traces */
+/*
+ * Update the transfer parameters.
+ * This function is executed from a work queue.
+ */
static void transfer_update(struct work_struct *work)
{
struct sd *sd = container_of(work, struct sd, work);
@@ -6079,96 +5938,55 @@ static void transfer_update(struct work_struct *work)
int change, good;
u8 reg07, reg11;
- /* synchronize with the main driver and initialize the registers */
- mutex_lock(&gspca_dev->usb_lock);
- reg07 = 0; /* max */
- reg_w(gspca_dev, reg07, 0x0007);
- reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
- mutex_unlock(&gspca_dev->usb_lock);
+ /* reg07 gets set to 0 by sd_start before starting us */
+ reg07 = 0;
good = 0;
for (;;) {
msleep(100);
- /* get the transfer status */
- /* the bit 0 of the bridge register 11 indicates overflow */
mutex_lock(&gspca_dev->usb_lock);
- if (!gspca_dev->present || !gspca_dev->streaming)
+#ifdef CONFIG_PM
+ if (gspca_dev->frozen)
goto err;
+#endif
+ if (!gspca_dev->dev || !gspca_dev->streaming)
+ goto err;
+
+ /* Bit 0 of register 11 indicates FIFO overflow */
+ gspca_dev->usb_err = 0;
reg11 = reg_r(gspca_dev, 0x0011);
- if (gspca_dev->usb_err < 0
- || !gspca_dev->present || !gspca_dev->streaming)
+ if (gspca_dev->usb_err)
goto err;
change = reg11 & 0x01;
if (change) { /* overflow */
- switch (reg07) {
- case 0: /* max */
- reg07 = sd->sensor == SENSOR_HV7131R
- ? 0x30 : 0x32;
- if (sd->reg08 != 0) {
- change = 3;
- sd->reg08--;
- }
- break;
- case 0x32:
- reg07 -= 4;
- break;
- default:
- reg07 -= 2;
- break;
- case 2:
- change = 0; /* already min */
- break;
- }
good = 0;
+
+ if (reg07 == 0) /* Bit Rate Control not enabled? */
+ reg07 = 0x32; /* Allow 98 bytes / unit */
+ else if (reg07 > 2)
+ reg07 -= 2; /* Decrease allowed bytes / unit */
+ else
+ change = 0;
} else { /* no overflow */
- if (reg07 != 0) { /* if not max */
- good++;
- if (good >= 10) {
- good = 0;
+ good++;
+ if (good >= 10) {
+ good = 0;
+ if (reg07) { /* BRC enabled? */
change = 1;
- reg07 += 2;
- switch (reg07) {
- case 0x30:
- if (sd->sensor == SENSOR_PAS202B)
- reg07 += 2;
- break;
- case 0x32:
- case 0x34:
+ if (reg07 < 0x32)
+ reg07 += 2;
+ else
reg07 = 0;
- break;
- }
- }
- } else { /* reg07 max */
- if (sd->reg08 < sizeof jpeg_qual - 1) {
- good++;
- if (good > 10) {
- sd->reg08++;
- change = 2;
- }
}
}
}
if (change) {
- if (change & 1) {
- reg_w(gspca_dev, reg07, 0x0007);
- if (gspca_dev->usb_err < 0
- || !gspca_dev->present
- || !gspca_dev->streaming)
- goto err;
- }
- if (change & 2) {
- reg_w(gspca_dev, sd->reg08,
- ZC3XX_R008_CLOCKSETTING);
- if (gspca_dev->usb_err < 0
- || !gspca_dev->present
- || !gspca_dev->streaming)
- goto err;
- sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08];
- jpeg_set_qual(sd->jpeg_hdr,
- jpeg_qual[sd->reg08]);
- }
+ gspca_dev->usb_err = 0;
+ reg_w(gspca_dev, reg07, 0x0007);
+ if (gspca_dev->usb_err)
+ goto err;
}
mutex_unlock(&gspca_dev->usb_lock);
}
@@ -6503,7 +6321,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* define some sensors from the vendor/product */
sd->sensor = id->driver_info;
- gspca_dev->cam.ctrls = sd->ctrls;
sd->reg08 = REG08_DEF;
INIT_WORK(&sd->work, transfer_update);
@@ -6511,12 +6328,87 @@ static int sd_config(struct gspca_dev *gspca_dev,
return 0;
}
-/* this function is called at probe and resume time */
-static int sd_init(struct gspca_dev *gspca_dev)
+static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct sd *sd = (struct sd *) gspca_dev;
- struct cam *cam;
- int sensor;
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUTOGAIN:
+ gspca_dev->usb_err = 0;
+ if (ctrl->val && sd->exposure && gspca_dev->streaming)
+ sd->exposure->val = getexposure(gspca_dev);
+ return gspca_dev->usb_err;
+ }
+ return -EINVAL;
+}
+
+static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct gspca_dev *gspca_dev =
+ container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
+ struct sd *sd = (struct sd *)gspca_dev;
+ int i, qual;
+
+ gspca_dev->usb_err = 0;
+
+ if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) {
+ qual = sd->reg08 >> 1;
+
+ for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) {
+ if (ctrl->val <= jpeg_qual[i])
+ break;
+ }
+ if (i > 0 && i == qual && ctrl->val < jpeg_qual[i])
+ i--;
+
+ /* With high quality settings we need max bandwidth */
+ if (i >= 2 && gspca_dev->streaming &&
+ !gspca_dev->cam.needs_full_bandwidth)
+ return -EBUSY;
+
+ sd->reg08 = (i << 1) | 1;
+ ctrl->val = jpeg_qual[i];
+ }
+
+ if (!gspca_dev->streaming)
+ return 0;
+
+ switch (ctrl->id) {
+ /* gamma/brightness/contrast cluster */
+ case V4L2_CID_GAMMA:
+ setcontrast(gspca_dev, sd->gamma->val,
+ sd->brightness->val, sd->contrast->val);
+ break;
+ /* autogain/exposure cluster */
+ case V4L2_CID_AUTOGAIN:
+ setautogain(gspca_dev, ctrl->val);
+ if (!gspca_dev->usb_err && !ctrl->val && sd->exposure)
+ setexposure(gspca_dev, sd->exposure->val);
+ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ setlightfreq(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_SHARPNESS:
+ setsharpness(gspca_dev, ctrl->val);
+ break;
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ setquality(gspca_dev);
+ break;
+ }
+ return gspca_dev->usb_err;
+}
+
+static const struct v4l2_ctrl_ops zcxx_ctrl_ops = {
+ .g_volatile_ctrl = zcxx_g_volatile_ctrl,
+ .s_ctrl = zcxx_s_ctrl,
+};
+
+static int sd_init_controls(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *)gspca_dev;
+ struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
static const u8 gamma[SENSOR_MAX] = {
[SENSOR_ADCM2700] = 4,
[SENSOR_CS2102] = 4,
@@ -6538,6 +6430,48 @@ static int sd_init(struct gspca_dev *gspca_dev)
[SENSOR_PO2030] = 4,
[SENSOR_TAS5130C] = 3,
};
+
+ gspca_dev->vdev.ctrl_handler = hdl;
+ v4l2_ctrl_handler_init(hdl, 8);
+ sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 128);
+ sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]);
+ if (sd->sensor == SENSOR_HV7131R)
+ sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927);
+ sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
+ if (sd->sensor != SENSOR_OV7630C)
+ sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_POWER_LINE_FREQUENCY,
+ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0,
+ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED);
+ sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0, 3, 1,
+ sd->sensor == SENSOR_PO2030 ? 0 : 2);
+ sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1,
+ jpeg_qual[REG08_DEF >> 1]);
+ if (hdl->error) {
+ pr_err("Could not initialize controls\n");
+ return hdl->error;
+ }
+ v4l2_ctrl_cluster(3, &sd->gamma);
+ if (sd->sensor == SENSOR_HV7131R)
+ v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true);
+ 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;
+ struct cam *cam;
+ int sensor;
static const u8 mode_tb[SENSOR_MAX] = {
[SENSOR_ADCM2700] = 2,
[SENSOR_CS2102] = 1,
@@ -6559,27 +6493,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
[SENSOR_PO2030] = 1,
[SENSOR_TAS5130C] = 1,
};
- static const u8 reg08_tb[SENSOR_MAX] = {
- [SENSOR_ADCM2700] = 1,
- [SENSOR_CS2102] = 3,
- [SENSOR_CS2102K] = 3,
- [SENSOR_GC0303] = 2,
- [SENSOR_GC0305] = 3,
- [SENSOR_HDCS2020] = 1,
- [SENSOR_HV7131B] = 3,
- [SENSOR_HV7131R] = 3,
- [SENSOR_ICM105A] = 3,
- [SENSOR_MC501CB] = 3,
- [SENSOR_MT9V111_1] = 3,
- [SENSOR_MT9V111_3] = 3,
- [SENSOR_OV7620] = 1,
- [SENSOR_OV7630C] = 3,
- [SENSOR_PAS106] = 3,
- [SENSOR_PAS202B] = 3,
- [SENSOR_PB0330] = 3,
- [SENSOR_PO2030] = 2,
- [SENSOR_TAS5130C] = 3,
- };
sensor = zcxx_probeSensor(gspca_dev);
if (sensor >= 0)
@@ -6688,7 +6601,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
case 0x2030:
PDEBUG(D_PROBE, "Find Sensor PO2030");
sd->sensor = SENSOR_PO2030;
- sd->ctrls[SHARPNESS].def = 0; /* from win traces */
break;
case 0x7620:
PDEBUG(D_PROBE, "Find Sensor OV7620");
@@ -6730,36 +6642,18 @@ static int sd_init(struct gspca_dev *gspca_dev)
break;
}
- sd->ctrls[GAMMA].def = gamma[sd->sensor];
- sd->reg08 = reg08_tb[sd->sensor];
- sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08];
- sd->ctrls[QUALITY].min = jpeg_qual[0];
- sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1];
-
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- gspca_dev->ctrl_dis = (1 << QUALITY);
- break;
- case SENSOR_OV7630C:
- gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE);
- break;
- case SENSOR_PAS202B:
- gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE);
- break;
- default:
- gspca_dev->ctrl_dis = (1 << EXPOSURE);
- break;
- }
-#if AUTOGAIN_DEF
- if (sd->ctrls[AUTOGAIN].val)
- gspca_dev->ctrl_inac = (1 << EXPOSURE);
-#endif
-
/* switch off the led */
reg_w(gspca_dev, 0x01, 0x0000);
return gspca_dev->usb_err;
}
+static int sd_pre_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0;
+ return 0;
+}
+
static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -6864,7 +6758,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x03, 0x0008);
break;
}
- setsharpness(gspca_dev);
+ setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness));
/* set the gamma tables when not set */
switch (sd->sensor) {
@@ -6873,7 +6767,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
case SENSOR_OV7630C:
break;
default:
- setcontrast(gspca_dev);
+ setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma),
+ v4l2_ctrl_g_ctrl(sd->brightness),
+ v4l2_ctrl_g_ctrl(sd->contrast));
break;
}
setmatrix(gspca_dev); /* one more time? */
@@ -6885,8 +6781,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
setquality(gspca_dev);
- jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]);
- setlightfreq(gspca_dev);
+ /* Start with BRC disabled, transfer_update will enable it if needed */
+ reg_w(gspca_dev, 0x00, 0x0007);
+ if (sd->plfreq)
+ setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq));
switch (sd->sensor) {
case SENSOR_ADCM2700:
@@ -6897,7 +6795,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x40, 0x0117);
break;
case SENSOR_HV7131R:
- setexposure(gspca_dev);
+ setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure));
reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
break;
case SENSOR_GC0305:
@@ -6921,21 +6819,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
break;
}
- setautogain(gspca_dev);
+ setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain));
- /* start the transfer update thread if needed */
- if (gspca_dev->usb_err >= 0) {
- switch (sd->sensor) {
- case SENSOR_HV7131R:
- case SENSOR_PAS202B:
- sd->work_thread =
- create_singlethread_workqueue(KBUILD_MODNAME);
- queue_work(sd->work_thread, &sd->work);
- break;
- }
- }
+ if (gspca_dev->usb_err < 0)
+ return gspca_dev->usb_err;
- return gspca_dev->usb_err;
+ /* Start the transfer parameters update thread */
+ sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME);
+ queue_work(sd->work_thread, &sd->work);
+
+ return 0;
}
/* called on streamoff with alt 0 and on disconnect */
@@ -6949,7 +6842,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
mutex_lock(&gspca_dev->usb_lock);
sd->work_thread = NULL;
}
- if (!gspca_dev->present)
+ if (!gspca_dev->dev)
return;
send_unknown(gspca_dev, sd->sensor);
}
@@ -6987,72 +6880,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->ctrls[AUTOGAIN].val = val;
- if (val) {
- gspca_dev->ctrl_inac |= (1 << EXPOSURE);
- } else {
- gspca_dev->ctrl_inac &= ~(1 << EXPOSURE);
- if (gspca_dev->streaming)
- getexposure(gspca_dev);
- }
- if (gspca_dev->streaming)
- setautogain(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_querymenu(struct gspca_dev *gspca_dev,
- struct v4l2_querymenu *menu)
-{
- switch (menu->id) {
- case V4L2_CID_POWER_LINE_FREQUENCY:
- switch (menu->index) {
- case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
- strcpy((char *) menu->name, "NoFliker");
- return 0;
- case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
- strcpy((char *) menu->name, "50 Hz");
- return 0;
- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
- strcpy((char *) menu->name, "60 Hz");
- return 0;
- }
- break;
- }
- return -EINVAL;
-}
-
-static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) {
- if (val <= jpeg_qual[i])
- break;
- }
- if (i > 0
- && i == sd->reg08
- && val < jpeg_qual[sd->reg08])
- i--;
- sd->reg08 = i;
- sd->ctrls[QUALITY].val = jpeg_qual[i];
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
- return gspca_dev->usb_err;
-}
-
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
+ int ret;
- sd_setquality(gspca_dev, jcomp->quality);
- jcomp->quality = sd->ctrls[QUALITY].val;
- return gspca_dev->usb_err;
+ ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality);
+ if (ret)
+ return ret;
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
+ return 0;
}
static int sd_get_jcomp(struct gspca_dev *gspca_dev,
@@ -7061,7 +6899,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->ctrls[QUALITY].val;
+ jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual);
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
return 0;
@@ -7085,14 +6923,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
static const struct sd_desc sd_desc = {
.name = KBUILD_MODNAME,
- .ctrls = sd_ctrls,
- .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
+ .init_controls = sd_init_controls,
+ .isoc_init = sd_pre_start,
.start = sd_start,
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
- .querymenu = sd_querymenu,
.get_jcomp = sd_get_jcomp,
.set_jcomp = sd_set_jcomp,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
@@ -7176,6 +7013,7 @@ static struct usb_driver sd_driver = {
#ifdef CONFIG_PM
.suspend = gspca_suspend,
.resume = gspca_resume,
+ .reset_resume = gspca_resume,
#endif
};
diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c
index 068df4ba3f5..ae8f229d114 100644
--- a/drivers/media/video/hdpvr/hdpvr-control.c
+++ b/drivers/media/video/hdpvr/hdpvr-control.c
@@ -113,6 +113,8 @@ int get_input_lines_info(struct hdpvr_device *dev)
"get input lines info returned: %d, %s\n", ret,
print_buf);
}
+#else
+ (void)ret; /* suppress compiler warning */
#endif
lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
mutex_unlock(&dev->usbc_mutex);
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
index 6510110f53d..304f43ef59e 100644
--- a/drivers/media/video/hdpvr/hdpvr-core.c
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -303,7 +303,7 @@ static int hdpvr_probe(struct usb_interface *interface,
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
- err("Out of memory");
+ dev_err(&interface->dev, "Out of memory\n");
goto error;
}
@@ -311,7 +311,7 @@ static int hdpvr_probe(struct usb_interface *interface,
/* register v4l2_device early so it can be used for printks */
if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
- err("v4l2_device_register failed");
+ dev_err(&interface->dev, "v4l2_device_register failed\n");
goto error;
}
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
index 11ffe9cc178..0e9e156bb2a 100644
--- a/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -994,7 +994,7 @@ static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
default:
return -EINVAL;
}
- return 0;
+ return ret;
}
static int vidioc_try_ext_ctrls(struct file *file, void *priv,
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index a62322d5c0d..366434f5647 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -40,15 +40,15 @@ static int hexium_num;
#define HEXIUM_INPUTS 9
static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
- { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
};
#define HEXIUM_AUDIOS 0
@@ -59,11 +59,6 @@ struct hexium_data
u8 byte;
};
-#define HEXIUM_CONTROLS 1
-static struct v4l2_queryctrl hexium_controls[] = {
- { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
-};
-
#define HEXIUM_GEMINI_V_1_0 1
#define HEXIUM_GEMINI_DUAL_V_1_0 2
@@ -76,7 +71,6 @@ struct hexium
int cur_input; /* current input */
v4l2_std_id cur_std; /* current standard */
- int cur_bw; /* current black/white status */
};
/* Samsung KS0127B decoder default registers */
@@ -119,18 +113,10 @@ static struct hexium_data hexium_pal[] = {
{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
};
-static struct hexium_data hexium_pal_bw[] = {
- { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
-};
-
static struct hexium_data hexium_ntsc[] = {
{ 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
};
-static struct hexium_data hexium_ntsc_bw[] = {
- { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF }
-};
-
static struct hexium_data hexium_secam[] = {
{ 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF }
};
@@ -264,93 +250,6 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
return 0;
}
-/* the saa7146 provides some controls (brightness, contrast, saturation)
- which gets registered *after* this function. because of this we have
- to return with a value != 0 even if the function succeeded.. */
-static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- int i;
-
- for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
- if (hexium_controls[i].id == qc->id) {
- *qc = hexium_controls[i];
- DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
- return 0;
- }
- }
- return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
-}
-
-static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct hexium *hexium = (struct hexium *) dev->ext_priv;
- int i;
-
- for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
- if (hexium_controls[i].id == vc->id)
- break;
- }
-
- if (i < 0)
- return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
-
- if (vc->id == V4L2_CID_PRIVATE_BASE) {
- vc->value = hexium->cur_bw;
- DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct hexium *hexium = (struct hexium *) dev->ext_priv;
- int i = 0;
-
- for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
- if (hexium_controls[i].id == vc->id)
- break;
- }
-
- if (i < 0)
- return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
-
- if (vc->id == V4L2_CID_PRIVATE_BASE)
- hexium->cur_bw = vc->value;
-
- DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw);
-
- if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
- hexium_set_standard(hexium, hexium_pal);
- return 0;
- }
- if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
- hexium_set_standard(hexium, hexium_ntsc);
- return 0;
- }
- if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
- hexium_set_standard(hexium, hexium_secam);
- return 0;
- }
- if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
- hexium_set_standard(hexium, hexium_pal_bw);
- return 0;
- }
- if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
- hexium_set_standard(hexium, hexium_ntsc_bw);
- return 0;
- }
- if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std)
- /* fixme: is there no bw secam mode? */
- return -EINVAL;
-
- return -EINVAL;
-}
-
-
static struct saa7146_ext_vv vv_data;
/* this function only gets called when the probing was successful */
@@ -399,12 +298,10 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
hexium->cur_input = 0;
saa7146_vv_init(dev, &vv_data);
- vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
- vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
- vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
- vv_data.ops.vidioc_enum_input = vidioc_enum_input;
- vv_data.ops.vidioc_g_input = vidioc_g_input;
- vv_data.ops.vidioc_s_input = vidioc_s_input;
+
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER);
if (ret < 0) {
pr_err("cannot register capture v4l2 device. skipping.\n");
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 23debc967d9..a1eb26d1107 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -41,15 +41,15 @@ static int hexium_num;
#define HEXIUM_INPUTS 9
static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = {
- { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
};
#define HEXIUM_AUDIOS 0
@@ -371,9 +371,9 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
DEB_EE("\n");
saa7146_vv_init(dev, &vv_data);
- vv_data.ops.vidioc_enum_input = vidioc_enum_input;
- vv_data.ops.vidioc_g_input = vidioc_g_input;
- vv_data.ops.vidioc_s_input = vidioc_s_input;
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
pr_err("cannot register capture v4l2 device. skipping.\n");
return -1;
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 679262ed13b..057929e165a 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -1201,9 +1201,9 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler;
itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
- V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0);
+ V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0);
itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
- V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0);
+ V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0);
/* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
mask that menu item. */
itv->ctrl_audio_playback =
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index c9663e885b9..9ff69b5a87e 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -746,8 +746,9 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait)
return res;
}
-unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
+unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait)
{
+ unsigned long req_events = poll_requested_events(wait);
struct ivtv_open_id *id = fh2id(filp->private_data);
struct ivtv *itv = id->itv;
struct ivtv_stream *s = &itv->streams[id->type];
@@ -755,7 +756,8 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait)
unsigned res = 0;
/* Start a capture if there is none */
- if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) &&
+ (req_events & (POLLIN | POLLRDNORM))) {
int rc;
rc = ivtv_start_capture(id);
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index 989e556913e..f7d57b3f284 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -135,7 +135,6 @@ void ivtv_set_osd_alpha(struct ivtv *itv)
int ivtv_set_speed(struct ivtv *itv, int speed)
{
u32 data[CX2341X_MBOX_MAX_DATA];
- struct ivtv_stream *s;
int single_step = (speed == 1 || speed == -1);
DEFINE_WAIT(wait);
@@ -145,8 +144,6 @@ int ivtv_set_speed(struct ivtv *itv, int speed)
if (speed == itv->speed && !single_step)
return 0;
- s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
-
if (single_step && (speed < 0) == (itv->speed < 0)) {
/* Single step video and no need to change direction */
ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0);
@@ -1468,8 +1465,9 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti
switch (sub->type) {
case V4L2_EVENT_VSYNC:
case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
case V4L2_EVENT_CTRL:
- return v4l2_event_subscribe(fh, sub, 0);
+ return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
default:
return -EINVAL;
}
@@ -1827,7 +1825,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio,
return ivtv_decoder_ioctls(file, cmd, (void *)arg);
default:
- return -EINVAL;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index 7ea5ca7f012..6738592aa35 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -228,6 +228,10 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
s->vdev->release = video_device_release;
s->vdev->tvnorms = V4L2_STD_ALL;
s->vdev->lock = &itv->serialize_lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &s->vdev->flags);
set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags);
ivtv_set_funcs(s->vdev);
return 0;
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index e5e7fa9e737..05b94aa8ba3 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -1293,6 +1293,7 @@ static int __init ivtvfb_init(void)
drv = driver_find("ivtv", &pci_bus_type);
err = driver_for_each_device(drv, NULL, &registered, ivtvfb_callback_init);
+ (void)err; /* suppress compiler warning */
if (!registered) {
printk(KERN_ERR "ivtvfb: no cards found\n");
return -ENODEV;
@@ -1309,6 +1310,7 @@ static void ivtvfb_cleanup(void)
drv = driver_find("ivtv", &pci_bus_type);
err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup);
+ (void)err; /* suppress compiler warning */
}
module_init(ivtvfb_init);
diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h
index 4b021e1ee5f..bb589917b65 100644
--- a/drivers/media/video/m5mols/m5mols.h
+++ b/drivers/media/video/m5mols/m5mols.h
@@ -21,11 +21,6 @@
extern int m5mols_debug;
-#define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd)
-
-#define to_sd(__ctrl) \
- (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd)
-
enum m5mols_restype {
M5MOLS_RESTYPE_MONITOR,
M5MOLS_RESTYPE_CAPTURE,
@@ -163,21 +158,27 @@ struct m5mols_version {
* @ffmt: current fmt according to resolution type
* @res_type: current resolution type
* @irq_waitq: waitqueue for the capture
- * @flags: state variable for the interrupt handler
+ * @irq_done: set to 1 in the interrupt handler
* @handle: control handler
- * @autoexposure: Auto Exposure control
- * @exposure: Exposure control
- * @autowb: Auto White Balance control
- * @colorfx: Color effect control
- * @saturation: Saturation control
- * @zoom: Zoom control
+ * @auto_exposure: auto/manual exposure control
+ * @exposure_bias: exposure compensation control
+ * @exposure: manual exposure control
+ * @metering: exposure metering control
+ * @auto_iso: auto/manual ISO sensitivity control
+ * @iso: manual ISO sensitivity control
+ * @auto_wb: auto white balance control
+ * @lock_3a: 3A lock control
+ * @colorfx: color effect control
+ * @saturation: saturation control
+ * @zoom: zoom control
+ * @wdr: wide dynamic range control
+ * @stabilization: image stabilization control
+ * @jpeg_quality: JPEG compression quality control
* @ver: information of the version
* @cap: the capture mode attributes
- * @power: current sensor's power status
* @isp_ready: 1 when the ISP controller has completed booting
+ * @power: current sensor's power status
* @ctrl_sync: 1 when the control handler state is restored in H/W
- * @lock_ae: true means the Auto Exposure is locked
- * @lock_awb: true means the Aut WhiteBalance is locked
* @resolution: register value for current resolution
* @mode: register value for current operation mode
* @set_power: optional power callback to the board code
@@ -193,15 +194,27 @@ struct m5mols_info {
atomic_t irq_done;
struct v4l2_ctrl_handler handle;
+ struct {
+ /* exposure/exposure bias/auto exposure cluster */
+ struct v4l2_ctrl *auto_exposure;
+ struct v4l2_ctrl *exposure_bias;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *metering;
+ };
+ struct {
+ /* iso/auto iso cluster */
+ struct v4l2_ctrl *auto_iso;
+ struct v4l2_ctrl *iso;
+ };
+ struct v4l2_ctrl *auto_wb;
- /* Autoexposure/exposure control cluster */
- struct v4l2_ctrl *autoexposure;
- struct v4l2_ctrl *exposure;
-
- struct v4l2_ctrl *autowb;
+ struct v4l2_ctrl *lock_3a;
struct v4l2_ctrl *colorfx;
struct v4l2_ctrl *saturation;
struct v4l2_ctrl *zoom;
+ struct v4l2_ctrl *wdr;
+ struct v4l2_ctrl *stabilization;
+ struct v4l2_ctrl *jpeg_quality;
struct m5mols_version ver;
struct m5mols_capture cap;
@@ -210,8 +223,6 @@ struct m5mols_info {
unsigned int power:1;
unsigned int ctrl_sync:1;
- bool lock_ae;
- bool lock_awb;
u8 resolution;
u8 mode;
@@ -282,7 +293,7 @@ int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask,
* The available executing order between each modes are as follows:
* PARAMETER <---> MONITOR <---> CAPTURE
*/
-int m5mols_mode(struct m5mols_info *info, u8 mode);
+int m5mols_set_mode(struct m5mols_info *info, u8 mode);
int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg);
int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout);
@@ -291,9 +302,33 @@ int m5mols_start_capture(struct m5mols_info *info);
int m5mols_do_scenemode(struct m5mols_info *info, u8 mode);
int m5mols_lock_3a(struct m5mols_info *info, bool lock);
int m5mols_set_ctrl(struct v4l2_ctrl *ctrl);
+int m5mols_init_controls(struct v4l2_subdev *sd);
/* The firmware function */
int m5mols_update_fw(struct v4l2_subdev *sd,
int (*set_power)(struct m5mols_info *, bool));
+static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct m5mols_info, sd);
+}
+
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ struct m5mols_info *info = container_of(ctrl->handler,
+ struct m5mols_info, handle);
+ return &info->sd;
+}
+
+static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl,
+ unsigned int mode)
+{
+ ctrl->priv = (void *)mode;
+}
+
+static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl)
+{
+ return (unsigned int)ctrl->priv;
+}
+
#endif /* M5MOLS_H */
diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c
index ba25e8e2ba4..cb243bd278c 100644
--- a/drivers/media/video/m5mols/m5mols_capture.c
+++ b/drivers/media/video/m5mols/m5mols_capture.c
@@ -106,7 +106,6 @@ static int m5mols_capture_info(struct m5mols_info *info)
int m5mols_start_capture(struct m5mols_info *info)
{
struct v4l2_subdev *sd = &info->sd;
- u8 resolution = info->resolution;
int ret;
/*
@@ -114,22 +113,18 @@ int m5mols_start_capture(struct m5mols_info *info)
* format. The frame capture is initiated during switching from Monitor
* to Capture mode.
*/
- ret = m5mols_mode(info, REG_MONITOR);
+ ret = m5mols_set_mode(info, REG_MONITOR);
if (!ret)
ret = m5mols_restore_controls(info);
if (!ret)
ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG);
if (!ret)
- ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution);
+ ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution);
if (!ret)
- ret = m5mols_lock_3a(info, true);
- if (!ret)
- ret = m5mols_mode(info, REG_CAPTURE);
+ ret = m5mols_set_mode(info, REG_CAPTURE);
if (!ret)
/* Wait until a frame is captured to ISP internal memory */
ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000);
- if (!ret)
- ret = m5mols_lock_3a(info, false);
if (ret)
return ret;
diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c
index d135d20d09c..392a028730e 100644
--- a/drivers/media/video/m5mols/m5mols_controls.c
+++ b/drivers/media/video/m5mols/m5mols_controls.c
@@ -139,7 +139,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode)
if (mode > REG_SCENE_CANDLE)
return -EINVAL;
- ret = m5mols_lock_3a(info, false);
+ ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0);
if (!ret)
ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode);
if (!ret)
@@ -169,7 +169,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode)
if (!ret)
ret = m5mols_write(sd, AE_ISO, scenemode.iso);
if (!ret)
- ret = m5mols_mode(info, REG_CAPTURE);
+ ret = m5mols_set_mode(info, REG_CAPTURE);
if (!ret)
ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr);
if (!ret)
@@ -181,119 +181,448 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode)
if (!ret)
ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode);
if (!ret)
- ret = m5mols_mode(info, REG_MONITOR);
+ ret = m5mols_set_mode(info, REG_MONITOR);
return ret;
}
-static int m5mols_lock_ae(struct m5mols_info *info, bool lock)
+static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl)
{
+ bool af_lock = ctrl->val & V4L2_LOCK_FOCUS;
int ret = 0;
- if (info->lock_ae != lock)
- ret = m5mols_write(&info->sd, AE_LOCK,
- lock ? REG_AE_LOCK : REG_AE_UNLOCK);
- if (!ret)
- info->lock_ae = lock;
+ if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) {
+ bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE;
+
+ ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ?
+ REG_AE_LOCK : REG_AE_UNLOCK);
+ if (ret)
+ return ret;
+ }
+
+ if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE)
+ && info->auto_wb->val) {
+ bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE;
+
+ ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ?
+ REG_AWB_LOCK : REG_AWB_UNLOCK);
+ if (ret)
+ return ret;
+ }
+
+ if (!info->ver.af || !af_lock)
+ return ret;
+
+ if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS)
+ ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
+
+ return ret;
+}
+
+static int m5mols_set_metering_mode(struct m5mols_info *info, int mode)
+{
+ unsigned int metering;
+
+ switch (mode) {
+ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED:
+ metering = REG_AE_CENTER;
+ break;
+ case V4L2_EXPOSURE_METERING_SPOT:
+ metering = REG_AE_SPOT;
+ break;
+ default:
+ metering = REG_AE_ALL;
+ break;
+ }
+
+ return m5mols_write(&info->sd, AE_MODE, metering);
+}
+
+static int m5mols_set_exposure(struct m5mols_info *info, int exposure)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ int ret = 0;
+
+ if (exposure == V4L2_EXPOSURE_AUTO) {
+ /* Unlock auto exposure */
+ info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE;
+ m5mols_3a_lock(info, info->lock_3a);
+
+ ret = m5mols_set_metering_mode(info, info->metering->val);
+ if (ret < 0)
+ return ret;
+
+ v4l2_dbg(1, m5mols_debug, sd,
+ "%s: exposure bias: %#x, metering: %#x\n",
+ __func__, info->exposure_bias->val,
+ info->metering->val);
+
+ return m5mols_write(sd, AE_INDEX, info->exposure_bias->val);
+ }
+
+ if (exposure == V4L2_EXPOSURE_MANUAL) {
+ ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
+ if (ret == 0)
+ ret = m5mols_write(sd, AE_MAN_GAIN_MON,
+ info->exposure->val);
+ if (ret == 0)
+ ret = m5mols_write(sd, AE_MAN_GAIN_CAP,
+ info->exposure->val);
+
+ v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n",
+ __func__, info->exposure->val);
+ }
+
+ return ret;
+}
+
+static int m5mols_set_white_balance(struct m5mols_info *info, int val)
+{
+ static const unsigned short wb[][2] = {
+ { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT },
+ { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 },
+ { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 },
+ { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON },
+ { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT },
+ { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT },
+ { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY },
+ { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE },
+ { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO },
+ };
+ int i;
+ struct v4l2_subdev *sd = &info->sd;
+ int ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(wb); i++) {
+ int awb;
+ if (wb[i][0] != val)
+ continue;
+
+ v4l2_dbg(1, m5mols_debug, sd,
+ "Setting white balance to: %#x\n", wb[i][0]);
+
+ awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO;
+ ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO :
+ REG_AWB_PRESET);
+ if (ret < 0)
+ return ret;
+
+ if (!awb)
+ ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]);
+ }
return ret;
}
-static int m5mols_lock_awb(struct m5mols_info *info, bool lock)
+static int m5mols_set_saturation(struct m5mols_info *info, int val)
+{
+ int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val);
+ if (ret < 0)
+ return ret;
+
+ return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON);
+}
+
+static int m5mols_set_color_effect(struct m5mols_info *info, int val)
{
+ unsigned int m_effect = REG_COLOR_EFFECT_OFF;
+ unsigned int p_effect = REG_EFFECT_OFF;
+ unsigned int cfix_r = 0, cfix_b = 0;
+ struct v4l2_subdev *sd = &info->sd;
int ret = 0;
- if (info->lock_awb != lock)
- ret = m5mols_write(&info->sd, AWB_LOCK,
- lock ? REG_AWB_LOCK : REG_AWB_UNLOCK);
+ switch (val) {
+ case V4L2_COLORFX_BW:
+ m_effect = REG_COLOR_EFFECT_ON;
+ break;
+ case V4L2_COLORFX_NEGATIVE:
+ p_effect = REG_EFFECT_NEGA;
+ break;
+ case V4L2_COLORFX_EMBOSS:
+ p_effect = REG_EFFECT_EMBOSS;
+ break;
+ case V4L2_COLORFX_SEPIA:
+ m_effect = REG_COLOR_EFFECT_ON;
+ cfix_r = REG_CFIXR_SEPIA;
+ cfix_b = REG_CFIXB_SEPIA;
+ break;
+ }
+
+ ret = m5mols_write(sd, PARM_EFFECT, p_effect);
if (!ret)
- info->lock_awb = lock;
+ ret = m5mols_write(sd, MON_EFFECT, m_effect);
+
+ if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) {
+ ret = m5mols_write(sd, MON_CFIXR, cfix_r);
+ if (!ret)
+ ret = m5mols_write(sd, MON_CFIXB, cfix_b);
+ }
+
+ v4l2_dbg(1, m5mols_debug, sd,
+ "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n",
+ p_effect, m_effect, cfix_r, cfix_b, ret);
return ret;
}
-/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */
-int m5mols_lock_3a(struct m5mols_info *info, bool lock)
+static int m5mols_set_iso(struct m5mols_info *info, int auto_iso)
+{
+ u32 iso = auto_iso ? 0 : info->iso->val + 1;
+
+ return m5mols_write(&info->sd, AE_ISO, iso);
+}
+
+static int m5mols_set_wdr(struct m5mols_info *info, int wdr)
{
int ret;
- ret = m5mols_lock_ae(info, lock);
- if (!ret)
- ret = m5mols_lock_awb(info, lock);
- /* Don't need to handle unlocking AF */
- if (!ret && is_available_af(info) && lock)
- ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP);
+ ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5);
+ if (ret < 0)
+ return ret;
+
+ ret = m5mols_set_mode(info, REG_CAPTURE);
+ if (ret < 0)
+ return ret;
+
+ return m5mols_write(&info->sd, CAPP_WDR_EN, wdr);
+}
+
+static int m5mols_set_stabilization(struct m5mols_info *info, int val)
+{
+ struct v4l2_subdev *sd = &info->sd;
+ unsigned int evp = val ? 0xe : 0x0;
+ int ret;
+
+ ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp);
+ if (ret < 0)
+ return ret;
+
+ return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp);
+}
+
+static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ struct m5mols_info *info = to_m5mols(sd);
+ int ret = 0;
+ u8 status;
+
+ v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n",
+ __func__, ctrl->name, info->isp_ready);
+
+ if (!info->isp_ready)
+ return -EBUSY;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ISO_SENSITIVITY_AUTO:
+ ret = m5mols_read_u8(sd, AE_ISO, &status);
+ if (ret == 0)
+ ctrl->val = !status;
+ if (status != REG_ISO_AUTO)
+ info->iso->val = status - 1;
+ break;
+
+ case V4L2_CID_3A_LOCK:
+ ctrl->val &= ~0x7;
+
+ ret = m5mols_read_u8(sd, AE_LOCK, &status);
+ if (ret)
+ return ret;
+ if (status)
+ info->lock_3a->val |= V4L2_LOCK_EXPOSURE;
+
+ ret = m5mols_read_u8(sd, AWB_LOCK, &status);
+ if (ret)
+ return ret;
+ if (status)
+ info->lock_3a->val |= V4L2_LOCK_EXPOSURE;
+
+ ret = m5mols_read_u8(sd, AF_EXECUTE, &status);
+ if (!status)
+ info->lock_3a->val |= V4L2_LOCK_EXPOSURE;
+ break;
+ }
return ret;
}
-/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */
-int m5mols_set_ctrl(struct v4l2_ctrl *ctrl)
+static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
{
+ unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl);
struct v4l2_subdev *sd = to_sd(ctrl);
struct m5mols_info *info = to_m5mols(sd);
- int ret;
+ int last_mode = info->mode;
+ int ret = 0;
+
+ /*
+ * If needed, defer restoring the controls until
+ * the device is fully initialized.
+ */
+ if (!info->isp_ready) {
+ info->ctrl_sync = 0;
+ return 0;
+ }
+
+ v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n",
+ __func__, ctrl->name, ctrl->val, (int)ctrl->priv);
+
+ if (ctrl_mode && ctrl_mode != info->mode) {
+ ret = m5mols_set_mode(info, ctrl_mode);
+ if (ret < 0)
+ return ret;
+ }
switch (ctrl->id) {
+ case V4L2_CID_3A_LOCK:
+ ret = m5mols_3a_lock(info, ctrl);
+ break;
+
case V4L2_CID_ZOOM_ABSOLUTE:
- return m5mols_write(sd, MON_ZOOM, ctrl->val);
+ ret = m5mols_write(sd, MON_ZOOM, ctrl->val);
+ break;
case V4L2_CID_EXPOSURE_AUTO:
- ret = m5mols_lock_ae(info,
- ctrl->val == V4L2_EXPOSURE_AUTO ? false : true);
- if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO)
- ret = m5mols_write(sd, AE_MODE, REG_AE_ALL);
- if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) {
- int val = info->exposure->val;
- ret = m5mols_write(sd, AE_MODE, REG_AE_OFF);
- if (!ret)
- ret = m5mols_write(sd, AE_MAN_GAIN_MON, val);
- if (!ret)
- ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val);
- }
- return ret;
+ ret = m5mols_set_exposure(info, ctrl->val);
+ break;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = m5mols_lock_awb(info, ctrl->val ? false : true);
- if (!ret)
- ret = m5mols_write(sd, AWB_MODE, ctrl->val ?
- REG_AWB_AUTO : REG_AWB_PRESET);
- return ret;
+ case V4L2_CID_ISO_SENSITIVITY:
+ ret = m5mols_set_iso(info, ctrl->val);
+ break;
+
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+ ret = m5mols_set_white_balance(info, ctrl->val);
+ break;
case V4L2_CID_SATURATION:
- ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val);
- if (!ret)
- ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON);
- return ret;
+ ret = m5mols_set_saturation(info, ctrl->val);
+ break;
case V4L2_CID_COLORFX:
- /*
- * This control uses two kinds of registers: normal & color.
- * The normal effect belongs to category 1, while the color
- * one belongs to category 2.
- *
- * The normal effect uses one register: CAT1_EFFECT.
- * The color effect uses three registers:
- * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB.
- */
- ret = m5mols_write(sd, PARM_EFFECT,
- ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA :
- ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS :
- REG_EFFECT_OFF);
- if (!ret)
- ret = m5mols_write(sd, MON_EFFECT,
- ctrl->val == V4L2_COLORFX_SEPIA ?
- REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF);
- if (!ret)
- ret = m5mols_write(sd, MON_CFIXR,
- ctrl->val == V4L2_COLORFX_SEPIA ?
- REG_CFIXR_SEPIA : 0);
- if (!ret)
- ret = m5mols_write(sd, MON_CFIXB,
- ctrl->val == V4L2_COLORFX_SEPIA ?
- REG_CFIXB_SEPIA : 0);
+ ret = m5mols_set_color_effect(info, ctrl->val);
+ break;
+
+ case V4L2_CID_WIDE_DYNAMIC_RANGE:
+ ret = m5mols_set_wdr(info, ctrl->val);
+ break;
+
+ case V4L2_CID_IMAGE_STABILIZATION:
+ ret = m5mols_set_stabilization(info, ctrl->val);
+ break;
+
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val);
+ break;
+ }
+
+ if (ret == 0 && info->mode != last_mode)
+ ret = m5mols_set_mode(info, last_mode);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
+ .g_volatile_ctrl = m5mols_g_volatile_ctrl,
+ .s_ctrl = m5mols_s_ctrl,
+};
+
+/* Supported manual ISO values */
+static const s64 iso_qmenu[] = {
+ /* AE_ISO: 0x01...0x07 */
+ 50, 100, 200, 400, 800, 1600, 3200
+};
+
+/* Supported Exposure Bias values, -2.0EV...+2.0EV */
+static const s64 ev_bias_qmenu[] = {
+ /* AE_INDEX: 0x00...0x08 */
+ -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000
+};
+
+int m5mols_init_controls(struct v4l2_subdev *sd)
+{
+ struct m5mols_info *info = to_m5mols(sd);
+ u16 exposure_max;
+ u16 zoom_step;
+ int ret;
+
+ /* Determine the firmware dependant control range and step values */
+ ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max);
+ if (ret < 0)
+ return ret;
+
+ zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
+ v4l2_ctrl_handler_init(&info->handle, 20);
+
+ info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
+ 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO);
+
+ /* Exposure control cluster */
+ info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
+ 1, ~0x03, V4L2_EXPOSURE_AUTO);
+
+ info->exposure = v4l2_ctrl_new_std(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
+ 0, exposure_max, 1, exposure_max / 2);
+
+ info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS,
+ ARRAY_SIZE(ev_bias_qmenu) - 1,
+ ARRAY_SIZE(ev_bias_qmenu)/2 - 1,
+ ev_bias_qmenu);
+
+ info->metering = v4l2_ctrl_new_std_menu(&info->handle,
+ &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING,
+ 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE);
+
+ /* ISO control cluster */
+ info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1);
+
+ info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1,
+ ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu);
+
+ info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_SATURATION, 1, 5, 1, 3);
+
+ info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1);
+
+ info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE);
+
+ info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0);
+
+ info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0);
+
+ info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80);
+
+ info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops,
+ V4L2_CID_3A_LOCK, 0, 0x7, 0, 0);
+
+ if (info->handle.error) {
+ int ret = info->handle.error;
+ v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
+ v4l2_ctrl_handler_free(&info->handle);
return ret;
}
- return -EINVAL;
+ v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false);
+ info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_UPDATE;
+ v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false);
+
+ info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER);
+ m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER);
+ m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR);
+
+ sd->ctrl_handler = &info->handle;
+
+ return 0;
}
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index d718aee01c7..ac7d28b6ddf 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -362,14 +362,14 @@ static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode)
}
/**
- * m5mols_mode - manage the M-5MOLS's mode
+ * m5mols_set_mode - set the M-5MOLS controller mode
* @mode: the required operation mode
*
* The commands of M-5MOLS are grouped into specific modes. Each functionality
- * can be guaranteed only when the sensor is operating in mode which which
- * a command belongs to.
+ * can be guaranteed only when the sensor is operating in mode which a command
+ * belongs to.
*/
-int m5mols_mode(struct m5mols_info *info, u8 mode)
+int m5mols_set_mode(struct m5mols_info *info, u8 mode)
{
struct v4l2_subdev *sd = &info->sd;
int ret = -EINVAL;
@@ -645,13 +645,13 @@ static int m5mols_start_monitor(struct m5mols_info *info)
struct v4l2_subdev *sd = &info->sd;
int ret;
- ret = m5mols_mode(info, REG_PARAMETER);
+ ret = m5mols_set_mode(info, REG_PARAMETER);
if (!ret)
ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution);
if (!ret)
ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30);
if (!ret)
- ret = m5mols_mode(info, REG_MONITOR);
+ ret = m5mols_set_mode(info, REG_MONITOR);
if (!ret)
ret = m5mols_restore_controls(info);
@@ -674,42 +674,13 @@ static int m5mols_s_stream(struct v4l2_subdev *sd, int enable)
return ret;
}
- return m5mols_mode(info, REG_PARAMETER);
+ return m5mols_set_mode(info, REG_PARAMETER);
}
static const struct v4l2_subdev_video_ops m5mols_video_ops = {
.s_stream = m5mols_s_stream,
};
-static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct v4l2_subdev *sd = to_sd(ctrl);
- struct m5mols_info *info = to_m5mols(sd);
- int ispstate = info->mode;
- int ret;
-
- /*
- * If needed, defer restoring the controls until
- * the device is fully initialized.
- */
- if (!info->isp_ready) {
- info->ctrl_sync = 0;
- return 0;
- }
-
- ret = m5mols_mode(info, REG_PARAMETER);
- if (ret < 0)
- return ret;
- ret = m5mols_set_ctrl(ctrl);
- if (ret < 0)
- return ret;
- return m5mols_mode(info, ispstate);
-}
-
-static const struct v4l2_ctrl_ops m5mols_ctrl_ops = {
- .s_ctrl = m5mols_s_ctrl,
-};
-
static int m5mols_sensor_power(struct m5mols_info *info, bool enable)
{
struct v4l2_subdev *sd = &info->sd;
@@ -802,52 +773,6 @@ static int m5mols_fw_start(struct v4l2_subdev *sd)
return ret;
}
-static int m5mols_init_controls(struct m5mols_info *info)
-{
- struct v4l2_subdev *sd = &info->sd;
- u16 max_exposure;
- u16 step_zoom;
- int ret;
-
- /* Determine value's range & step of controls for various FW version */
- ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure);
- if (!ret)
- step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1;
- if (ret)
- return ret;
-
- v4l2_ctrl_handler_init(&info->handle, 6);
- info->autowb = v4l2_ctrl_new_std(&info->handle,
- &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE,
- 0, 1, 1, 0);
- info->saturation = v4l2_ctrl_new_std(&info->handle,
- &m5mols_ctrl_ops, V4L2_CID_SATURATION,
- 1, 5, 1, 3);
- info->zoom = v4l2_ctrl_new_std(&info->handle,
- &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE,
- 1, 70, step_zoom, 1);
- info->exposure = v4l2_ctrl_new_std(&info->handle,
- &m5mols_ctrl_ops, V4L2_CID_EXPOSURE,
- 0, max_exposure, 1, (int)max_exposure/2);
- info->colorfx = v4l2_ctrl_new_std_menu(&info->handle,
- &m5mols_ctrl_ops, V4L2_CID_COLORFX,
- 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE);
- info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle,
- &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
- 1, 0, V4L2_EXPOSURE_AUTO);
-
- sd->ctrl_handler = &info->handle;
- if (info->handle.error) {
- v4l2_err(sd, "Failed to initialize controls: %d\n", ret);
- v4l2_ctrl_handler_free(&info->handle);
- return info->handle.error;
- }
-
- v4l2_ctrl_cluster(2, &info->autoexposure);
-
- return 0;
-}
-
/**
* m5mols_s_power - Main sensor power control function
*
@@ -868,7 +793,7 @@ static int m5mols_s_power(struct v4l2_subdev *sd, int on)
}
if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) {
- ret = m5mols_mode(info, REG_MONITOR);
+ ret = m5mols_set_mode(info, REG_MONITOR);
if (!ret)
ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP);
if (!ret)
@@ -1010,7 +935,7 @@ static int __devinit m5mols_probe(struct i2c_client *client,
ret = m5mols_fw_start(sd);
if (!ret)
- ret = m5mols_init_controls(info);
+ ret = m5mols_init_controls(sd);
m5mols_sensor_power(info, false);
if (!ret)
diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h
index ae4aced0f9b..14d4be72aef 100644
--- a/drivers/media/video/m5mols/m5mols_reg.h
+++ b/drivers/media/video/m5mols/m5mols_reg.h
@@ -310,6 +310,7 @@
#define REG_JPEG 0x10
#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1)
+#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1)
#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1)
#define REG_MCC_OFF 0x00
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
index 996ac34d9a8..ce2b7b4788d 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.c
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -1356,7 +1356,6 @@ static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
goto out;
}
mcam_set_config_needed(cam, 1);
- ret = 0;
out:
mutex_unlock(&cam->s_mutex);
return ret;
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index 12897e8a331..d2dec585e61 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -40,7 +40,7 @@ MODULE_VERSION("0.1.1");
#define MIN_H 32
#define MAX_W 640
#define MAX_H 480
-#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */
+#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */
/* Flags that indicate a format can be used for capture/output */
#define MEM2MEM_CAPTURE (1 << 0)
@@ -958,6 +958,10 @@ static int m2mtest_probe(struct platform_device *pdev)
}
*vfd = m2mtest_videodev;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
vfd->lock = &dev->dev_mutex;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
index b09a3c80a15..7bc775219f9 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/video/meye.c
@@ -1570,7 +1570,7 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio,
return meyeioc_stilljcapt((int *) arg);
default:
- return -EINVAL;
+ return -ENOTTY;
}
}
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index 82ce50721de..aeb22be7dcb 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -597,19 +597,23 @@ static int msp_log_status(struct v4l2_subdev *sd)
return 0;
}
-static int msp_suspend(struct i2c_client *client, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int msp_suspend(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
v4l_dbg(1, msp_debug, client, "suspend\n");
msp_reset(client);
return 0;
}
-static int msp_resume(struct i2c_client *client)
+static int msp_resume(struct device *dev)
{
+ struct i2c_client *client = to_i2c_client(dev);
v4l_dbg(1, msp_debug, client, "resume\n");
msp_wake_thread(client);
return 0;
}
+#endif
/* ----------------------------------------------------------------------- */
@@ -863,6 +867,10 @@ static int msp_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
+static const struct dev_pm_ops msp3400_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume)
+};
+
static const struct i2c_device_id msp_id[] = {
{ "msp3400", 0 },
{ }
@@ -873,11 +881,10 @@ static struct i2c_driver msp_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "msp3400",
+ .pm = &msp3400_pm_ops,
},
.probe = msp_probe,
.remove = msp_remove,
- .suspend = msp_suspend,
- .resume = msp_resume,
.id_table = msp_id,
};
diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c
index 645973c5feb..3c1e626139b 100644
--- a/drivers/media/video/mt9m032.c
+++ b/drivers/media/video/mt9m032.c
@@ -838,9 +838,9 @@ static int mt9m032_remove(struct i2c_client *client)
struct v4l2_subdev *subdev = i2c_get_clientdata(client);
struct mt9m032 *sensor = to_mt9m032(subdev);
- v4l2_device_unregister_subdev(&sensor->subdev);
+ v4l2_device_unregister_subdev(subdev);
v4l2_ctrl_handler_free(&sensor->ctrls);
- media_entity_cleanup(&sensor->subdev.entity);
+ media_entity_cleanup(&subdev->entity);
mutex_destroy(&sensor->lock);
kfree(sensor);
return 0;
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
index c81eaf4fbe0..8f061d9ac44 100644
--- a/drivers/media/video/mt9p031.c
+++ b/drivers/media/video/mt9p031.c
@@ -14,6 +14,7 @@
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/log2.h>
@@ -90,7 +91,14 @@
#define MT9P031_GLOBAL_GAIN_MAX 1024
#define MT9P031_GLOBAL_GAIN_DEF 8
#define MT9P031_GLOBAL_GAIN_MULT (1 << 6)
+#define MT9P031_ROW_BLACK_TARGET 0x49
#define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b
+#define MT9P031_GREEN1_OFFSET 0x60
+#define MT9P031_GREEN2_OFFSET 0x61
+#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62
+#define MT9P031_BLC_MANUAL_BLC (1 << 0)
+#define MT9P031_RED_OFFSET 0x63
+#define MT9P031_BLUE_OFFSET 0x64
#define MT9P031_TEST_PATTERN 0xa0
#define MT9P031_TEST_PATTERN_SHIFT 3
#define MT9P031_TEST_PATTERN_ENABLE (1 << 0)
@@ -99,17 +107,27 @@
#define MT9P031_TEST_PATTERN_RED 0xa2
#define MT9P031_TEST_PATTERN_BLUE 0xa3
+enum mt9p031_model {
+ MT9P031_MODEL_COLOR,
+ MT9P031_MODEL_MONOCHROME,
+};
+
struct mt9p031 {
struct v4l2_subdev subdev;
struct media_pad pad;
struct v4l2_rect crop; /* Sensor window */
struct v4l2_mbus_framefmt format;
- struct v4l2_ctrl_handler ctrls;
struct mt9p031_platform_data *pdata;
struct mutex power_lock; /* lock to protect power_count */
int power_count;
+ enum mt9p031_model model;
struct aptina_pll pll;
+ int reset;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *blc_auto;
+ struct v4l2_ctrl *blc_offset;
/* Registers cache */
u16 output_control;
@@ -241,8 +259,8 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031)
static int mt9p031_power_on(struct mt9p031 *mt9p031)
{
/* Ensure RESET_BAR is low */
- if (mt9p031->pdata->reset) {
- mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ if (mt9p031->reset != -1) {
+ gpio_set_value(mt9p031->reset, 0);
usleep_range(1000, 2000);
}
@@ -252,8 +270,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031)
mt9p031->pdata->ext_freq);
/* Now RESET_BAR must be high */
- if (mt9p031->pdata->reset) {
- mt9p031->pdata->reset(&mt9p031->subdev, 0);
+ if (mt9p031->reset != -1) {
+ gpio_set_value(mt9p031->reset, 1);
usleep_range(1000, 2000);
}
@@ -262,8 +280,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031)
static void mt9p031_power_off(struct mt9p031 *mt9p031)
{
- if (mt9p031->pdata->reset) {
- mt9p031->pdata->reset(&mt9p031->subdev, 1);
+ if (mt9p031->reset != -1) {
+ gpio_set_value(mt9p031->reset, 0);
usleep_range(1000, 2000);
}
@@ -557,6 +575,10 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev,
*/
#define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001)
+#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002)
+#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003)
+#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004)
+#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005)
static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
{
@@ -621,11 +643,17 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_TEST_PATTERN:
if (!ctrl->val) {
- ret = mt9p031_set_mode2(mt9p031,
- 0, MT9P031_READ_MODE_2_ROW_BLC);
- if (ret < 0)
- return ret;
-
+ /* Restore the black level compensation settings. */
+ if (mt9p031->blc_auto->cur.val != 0) {
+ ret = mt9p031_s_ctrl(mt9p031->blc_auto);
+ if (ret < 0)
+ return ret;
+ }
+ if (mt9p031->blc_offset->cur.val != 0) {
+ ret = mt9p031_s_ctrl(mt9p031->blc_offset);
+ if (ret < 0)
+ return ret;
+ }
return mt9p031_write(client, MT9P031_TEST_PATTERN,
MT9P031_TEST_PATTERN_DISABLE);
}
@@ -640,10 +668,14 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
if (ret < 0)
return ret;
+ /* Disable digital black level compensation when using a test
+ * pattern.
+ */
ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC,
0);
if (ret < 0)
return ret;
+
ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0);
if (ret < 0)
return ret;
@@ -651,7 +683,40 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
return mt9p031_write(client, MT9P031_TEST_PATTERN,
((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT)
| MT9P031_TEST_PATTERN_ENABLE);
+
+ case V4L2_CID_BLC_AUTO:
+ ret = mt9p031_set_mode2(mt9p031,
+ ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC,
+ ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0);
+ if (ret < 0)
+ return ret;
+
+ return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION,
+ ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC);
+
+ case V4L2_CID_BLC_TARGET_LEVEL:
+ return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET,
+ ctrl->val);
+
+ case V4L2_CID_BLC_ANALOG_OFFSET:
+ data = ctrl->val & ((1 << 9) - 1);
+
+ ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data);
+ if (ret < 0)
+ return ret;
+ ret = mt9p031_write(client, MT9P031_RED_OFFSET, data);
+ if (ret < 0)
+ return ret;
+ return mt9p031_write(client, MT9P031_BLUE_OFFSET, data);
+
+ case V4L2_CID_BLC_DIGITAL_OFFSET:
+ return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET,
+ ctrl->val & ((1 << 12) - 1));
}
+
return 0;
}
@@ -685,6 +750,46 @@ static const struct v4l2_ctrl_config mt9p031_ctrls[] = {
.flags = 0,
.menu_skip_mask = 0,
.qmenu = mt9p031_test_pattern_menu,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_AUTO,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "BLC, Auto",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 1,
+ .flags = 0,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_TARGET_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "BLC Target Level",
+ .min = 0,
+ .max = 4095,
+ .step = 1,
+ .def = 168,
+ .flags = 0,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_ANALOG_OFFSET,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "BLC Analog Offset",
+ .min = -255,
+ .max = 255,
+ .step = 1,
+ .def = 32,
+ .flags = 0,
+ }, {
+ .ops = &mt9p031_ctrl_ops,
+ .id = V4L2_CID_BLC_DIGITAL_OFFSET,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "BLC Digital Offset",
+ .min = -2048,
+ .max = 2047,
+ .step = 1,
+ .def = 40,
+ .flags = 0,
}
};
@@ -764,7 +869,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
format = v4l2_subdev_get_try_format(fh, 0);
- if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
format->code = V4L2_MBUS_FMT_Y12_1X12;
else
format->code = V4L2_MBUS_FMT_SGRBG12_1X12;
@@ -842,6 +947,8 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->pdata = pdata;
mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF;
mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC;
+ mt9p031->model = did->driver_data;
+ mt9p031->reset = -1;
v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4);
@@ -862,9 +969,16 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->subdev.ctrl_handler = &mt9p031->ctrls;
- if (mt9p031->ctrls.error)
+ if (mt9p031->ctrls.error) {
printk(KERN_INFO "%s: control initialization error %d\n",
__func__, mt9p031->ctrls.error);
+ ret = mt9p031->ctrls.error;
+ goto done;
+ }
+
+ mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO);
+ mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls,
+ V4L2_CID_BLC_DIGITAL_OFFSET);
mutex_init(&mt9p031->power_lock);
v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
@@ -882,7 +996,7 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->crop.left = MT9P031_COLUMN_START_DEF;
mt9p031->crop.top = MT9P031_ROW_START_DEF;
- if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
+ if (mt9p031->model == MT9P031_MODEL_MONOCHROME)
mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
else
mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
@@ -892,10 +1006,22 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->format.field = V4L2_FIELD_NONE;
mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
+ if (pdata->reset != -1) {
+ ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW,
+ "mt9p031_rst");
+ if (ret < 0)
+ goto done;
+
+ mt9p031->reset = pdata->reset;
+ }
+
ret = mt9p031_pll_setup(mt9p031);
done:
if (ret < 0) {
+ if (mt9p031->reset != -1)
+ gpio_free(mt9p031->reset);
+
v4l2_ctrl_handler_free(&mt9p031->ctrls);
media_entity_cleanup(&mt9p031->subdev.entity);
kfree(mt9p031);
@@ -912,13 +1038,16 @@ static int mt9p031_remove(struct i2c_client *client)
v4l2_ctrl_handler_free(&mt9p031->ctrls);
v4l2_device_unregister_subdev(subdev);
media_entity_cleanup(&subdev->entity);
+ if (mt9p031->reset != -1)
+ gpio_free(mt9p031->reset);
kfree(mt9p031);
return 0;
}
static const struct i2c_device_id mt9p031_id[] = {
- { "mt9p031", 0 },
+ { "mt9p031", MT9P031_MODEL_COLOR },
+ { "mt9p031m", MT9P031_MODEL_MONOCHROME },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9p031_id);
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index 8d1445f1270..e1ae46a7ee9 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -453,6 +453,7 @@ static int mt9t112_init_pll(const struct i2c_client *client)
* I2C Master Clock Divider
*/
mt9t112_reg_write(ret, client, 0x0014, 0x3046);
+ mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */
mt9t112_reg_write(ret, client, 0x0022, 0x0190);
mt9t112_reg_write(ret, client, 0x3B84, 0x0212);
diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c
index 75e253a343c..4ba4884c016 100644
--- a/drivers/media/video/mt9v032.c
+++ b/drivers/media/video/mt9v032.c
@@ -481,7 +481,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_EXPOSURE_AUTO:
return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE,
- ctrl->val);
+ !ctrl->val);
case V4L2_CID_EXPOSURE:
return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH,
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index 055d11ddb03..4296a835029 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -126,13 +126,8 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- if (bytes_per_line < 0)
- return bytes_per_line;
-
- *size = bytes_per_line * icd->user_height;
+ *size = icd->sizeimage;
if (!*count)
*count = 32;
@@ -171,11 +166,6 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
struct soc_camera_device *icd = vq->priv_data;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
int ret;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
@@ -202,7 +192,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
vb->state = VIDEOBUF_NEEDS_INIT;
}
- vb->size = bytes_per_line * vb->height;
+ vb->size = icd->sizeimage;
if (0 != vb->baddr && vb->bsize < vb->size) {
ret = -EINVAL;
goto out;
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index 18afaeeadb7..ded26b7286f 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -22,6 +22,7 @@
#include <linux/gcd.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/math64.h>
#include <linux/mm.h>
#include <linux/moduleparam.h>
#include <linux/time.h>
@@ -344,6 +345,19 @@ static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
PRP_INTR_CH2OVF,
}
},
+ {
+ .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8,
+ .out_fmt = V4L2_PIX_FMT_YUV420,
+ .cfg = {
+ .channel = 2,
+ .in_fmt = PRP_CNTL_DATA_IN_YUV422,
+ .out_fmt = PRP_CNTL_CH2_OUT_YUV420,
+ .src_pixel = 0x22000888, /* YUV422 (YUYV) */
+ .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC | PRP_INTR_LBOVF |
+ PRP_INTR_CH2OVF,
+ }
+ },
};
static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
@@ -525,8 +539,6 @@ static int mx2_videobuf_setup(struct vb2_queue *vq,
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
@@ -534,12 +546,9 @@ static int mx2_videobuf_setup(struct vb2_queue *vq,
if (fmt != NULL)
return -ENOTTY;
- if (bytes_per_line < 0)
- return bytes_per_line;
-
alloc_ctxs[0] = pcdev->alloc_ctx;
- sizes[0] = bytes_per_line * icd->user_height;
+ sizes[0] = icd->sizeimage;
if (0 == *count)
*count = 32;
@@ -555,16 +564,11 @@ static int mx2_videobuf_setup(struct vb2_queue *vq,
static int mx2_videobuf_prepare(struct vb2_buffer *vb)
{
struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
int ret = 0;
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
- if (bytes_per_line < 0)
- return bytes_per_line;
-
#ifdef DEBUG
/*
* This can be useful if you want to see if we actually fill
@@ -574,7 +578,7 @@ static int mx2_videobuf_prepare(struct vb2_buffer *vb)
0xaa, vb2_get_plane_payload(vb, 0));
#endif
- vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height);
+ vb2_set_plane_payload(vb, 0, icd->sizeimage);
if (vb2_plane_vaddr(vb, 0) &&
vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
ret = -EINVAL;
@@ -980,6 +984,7 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
+ const struct soc_camera_format_xlate *xlate;
unsigned long common_flags;
int ret;
int bytesperline;
@@ -1024,14 +1029,31 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
return ret;
}
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(icd->parent, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+ if (xlate->code == V4L2_MBUS_FMT_YUYV8_2X8) {
+ csicr1 |= CSICR1_PACK_DIR;
+ csicr1 &= ~CSICR1_SWAP16_EN;
+ dev_dbg(icd->parent, "already yuyv format, don't convert\n");
+ } else if (xlate->code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ csicr1 &= ~CSICR1_PACK_DIR;
+ csicr1 |= CSICR1_SWAP16_EN;
+ dev_dbg(icd->parent, "convert uyvy mbus format into yuyv\n");
+ } else {
+ dev_warn(icd->parent, "mbus format not supported\n");
+ return -EINVAL;
+ }
+
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
csicr1 |= CSICR1_REDGE;
if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_SOF_POL;
if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
csicr1 |= CSICR1_HSYNC_POL;
- if (pcdev->platform_flags & MX2_CAMERA_SWAP16)
- csicr1 |= CSICR1_SWAP16_EN;
if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC)
csicr1 |= CSICR1_EXT_VSYNC;
if (pcdev->platform_flags & MX2_CAMERA_CCIR)
@@ -1042,8 +1064,6 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
csicr1 |= CSICR1_GCLK_MODE;
if (pcdev->platform_flags & MX2_CAMERA_INV_DATA)
csicr1 |= CSICR1_INV_DATA;
- if (pcdev->platform_flags & MX2_CAMERA_PACK_DIR_MSB)
- csicr1 |= CSICR1_PACK_DIR;
pcdev->csicr1 = csicr1;
@@ -1118,7 +1138,8 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd,
return 0;
}
- if (code == V4L2_MBUS_FMT_YUYV8_2X8) {
+ if (code == V4L2_MBUS_FMT_YUYV8_2X8 ||
+ code == V4L2_MBUS_FMT_UYVY8_2X8) {
formats++;
if (xlate) {
/*
@@ -1134,6 +1155,18 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd,
}
}
+ if (code == V4L2_MBUS_FMT_UYVY8_2X8) {
+ formats++;
+ if (xlate) {
+ xlate->host_fmt =
+ soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8);
+ xlate->code = code;
+ dev_dbg(dev, "Providing host format %s for sensor code %d\n",
+ xlate->host_fmt->name, code);
+ xlate++;
+ }
+ }
+
/* Generic pass-trough */
formats++;
if (xlate) {
@@ -1363,17 +1396,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
xlate->host_fmt);
if (pix->bytesperline < 0)
return pix->bytesperline;
- pix->sizeimage = pix->height * pix->bytesperline;
+ pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+ pix->bytesperline, pix->height);
/* Check against the CSIRXCNT limit */
if (pix->sizeimage > 4 * 0x3ffff) {
/* Adjust geometry, preserve aspect ratio */
- unsigned int new_height = int_sqrt(4 * 0x3ffff *
- pix->height / pix->bytesperline);
+ unsigned int new_height = int_sqrt(div_u64(0x3ffffULL *
+ 4 * pix->height, pix->bytesperline));
pix->width = new_height * pix->width / pix->height;
pix->height = new_height;
pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
xlate->host_fmt);
BUG_ON(pix->bytesperline < 0);
+ pix->sizeimage = soc_mbus_image_size(xlate->host_fmt,
+ pix->bytesperline, pix->height);
}
}
@@ -1752,6 +1788,8 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
pcdev->soc_host.priv = pcdev;
pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
+ if (cpu_is_mx25())
+ pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE;
pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(pcdev->alloc_ctx)) {
diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c
index ba89a7401c8..0bd5815de36 100644
--- a/drivers/media/video/mx2_emmaprp.c
+++ b/drivers/media/video/mx2_emmaprp.c
@@ -755,7 +755,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
memset(src_vq, 0, sizeof(*src_vq));
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- src_vq->io_modes = VB2_MMAP;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->ops = &emmaprp_qops;
@@ -767,7 +767,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
memset(dst_vq, 0, sizeof(*dst_vq));
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- dst_vq->io_modes = VB2_MMAP;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->ops = &emmaprp_qops;
@@ -904,6 +904,10 @@ static int emmaprp_probe(struct platform_device *pdev)
}
*vfd = emmaprp_videodev;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
vfd->lock = &pcdev->dev_mutex;
video_set_drvdata(vfd, pcdev);
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index 93c35ef5f0a..02d54a057b6 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -199,8 +199,6 @@ static int mx3_videobuf_setup(struct vb2_queue *vq,
struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- int bytes_per_line;
- unsigned int height;
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
@@ -208,21 +206,29 @@ static int mx3_videobuf_setup(struct vb2_queue *vq,
if (fmt) {
const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
fmt->fmt.pix.pixelformat);
+ unsigned int bytes_per_line;
+ int ret;
+
if (!xlate)
return -EINVAL;
- bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
- xlate->host_fmt);
- height = fmt->fmt.pix.height;
+
+ ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+
+ ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+ fmt->fmt.pix.height);
+ if (ret < 0)
+ return ret;
+
+ sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
} else {
/* Called from VIDIOC_REQBUFS or in compatibility mode */
- bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- height = icd->user_height;
+ sizes[0] = icd->sizeimage;
}
- if (bytes_per_line < 0)
- return bytes_per_line;
-
- sizes[0] = bytes_per_line * height;
alloc_ctxs[0] = mx3_cam->alloc_ctx;
@@ -267,14 +273,11 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
struct idmac_video_param *video = &ichan->params.video;
const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt);
unsigned long flags;
dma_cookie_t cookie;
size_t new_size;
- BUG_ON(bytes_per_line <= 0);
-
- new_size = bytes_per_line * icd->user_height;
+ new_size = icd->sizeimage;
if (vb2_plane_size(vb, 0) < new_size) {
dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n",
@@ -314,9 +317,9 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
* horizontal parameters in this case are expressed in bytes,
* not in pixels.
*/
- video->out_width = bytes_per_line;
+ video->out_width = icd->bytesperline;
video->out_height = icd->user_height;
- video->out_stride = bytes_per_line;
+ video->out_stride = icd->bytesperline;
} else {
/*
* For IPU known formats the pixel unit will be managed
@@ -642,12 +645,14 @@ static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
}, {
.fourcc = V4L2_PIX_FMT_GREY,
.name = "Monochrome 8 bit",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
};
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index 2e413174843..b520a45cb3f 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -31,10 +31,11 @@
#include <media/saa7115.h>
#include <linux/module.h>
-#include "mxb.h"
#include "tea6415c.h"
#include "tea6420.h"
+#define MXB_AUDIOS 6
+
#define I2C_SAA7111A 0x24
#define I2C_TDA9840 0x42
#define I2C_TEA6415C 0x43
@@ -62,10 +63,14 @@ MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off).");
enum { TUNER, AUX1, AUX3, AUX3_YC };
static struct v4l2_input mxb_inputs[MXB_INPUTS] = {
- { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
- { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD },
+ { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0,
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD },
+ { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
+ { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0,
+ V4L2_STD_ALL, 0, V4L2_IN_CAP_STD },
};
/* this array holds the information, which port of the saa7146 each
@@ -90,6 +95,36 @@ struct mxb_routing {
u32 output;
};
+/* these are the available audio sources, which can switched
+ to the line- and cd-output individually */
+static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
+ {
+ .index = 0,
+ .name = "Tuner",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 1,
+ .name = "AUX1",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 2,
+ .name = "AUX2",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 3,
+ .name = "AUX3",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 4,
+ .name = "Radio (X9)",
+ .capability = V4L2_AUDCAP_STEREO,
+ } , {
+ .index = 5,
+ .name = "CD-ROM (X10)",
+ .capability = V4L2_AUDCAP_STEREO,
+ }
+};
+
/* These are the necessary input-output-pins for bringing one audio source
(see above) to the CD-output. Note that gain is set to 0 in this table. */
static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
@@ -114,11 +149,6 @@ static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
{ { 6, 3 }, { 6, 2 } } /* Mute */
};
-#define MAXCONTROLS 1
-static struct v4l2_queryctrl mxb_controls[] = {
- { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 },
-};
-
struct mxb
{
struct video_device *video_dev;
@@ -135,6 +165,7 @@ struct mxb
int cur_mode; /* current audio mode (mono, stereo, ...) */
int cur_input; /* current input */
+ int cur_audinput; /* current audio input */
int cur_mute; /* current mute status */
struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
};
@@ -150,16 +181,21 @@ struct mxb
#define call_all(dev, o, f, args...) \
v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
-static inline void tea6420_route_cd(struct mxb *mxb, int idx)
+static void mxb_update_audmode(struct mxb *mxb)
+{
+ struct v4l2_tuner t = {
+ .audmode = mxb->cur_mode,
+ };
+
+ tda9840_call(mxb, tuner, s_tuner, &t);
+}
+
+static inline void tea6420_route(struct mxb *mxb, int idx)
{
v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0);
v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0);
-}
-
-static inline void tea6420_route_line(struct mxb *mxb, int idx)
-{
v4l2_subdev_call(mxb->tea6420_1, audio, s_routing,
TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0);
v4l2_subdev_call(mxb->tea6420_2, audio, s_routing,
@@ -168,16 +204,45 @@ static inline void tea6420_route_line(struct mxb *mxb, int idx)
static struct saa7146_extension extension;
+static int mxb_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct saa7146_dev *dev = container_of(ctrl->handler,
+ struct saa7146_dev, ctrl_handler);
+ struct mxb *mxb = dev->ext_priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ mxb->cur_mute = ctrl->val;
+ /* switch the audio-source */
+ tea6420_route(mxb, ctrl->val ? 6 :
+ video_audio_connect[mxb->cur_input]);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops mxb_ctrl_ops = {
+ .s_ctrl = mxb_s_ctrl,
+};
+
static int mxb_probe(struct saa7146_dev *dev)
{
+ struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
struct mxb *mxb = NULL;
+ v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ if (hdl->error)
+ return hdl->error;
mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
if (mxb == NULL) {
DEB_D("not enough kernel memory\n");
return -ENOMEM;
}
+
snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
@@ -214,6 +279,8 @@ static int mxb_probe(struct saa7146_dev *dev)
/* we store the pointer in our private data field */
dev->ext_priv = mxb;
+ v4l2_ctrl_handler_setup(hdl);
+
return 0;
}
@@ -286,6 +353,9 @@ static int mxb_init_done(struct saa7146_dev* dev)
int i = 0, err = 0;
+ /* mute audio on tea6420s */
+ tea6420_route(mxb, 6);
+
/* select video mode in saa7111a */
saa7111a_call(mxb, core, s_std, std);
@@ -306,12 +376,12 @@ static int mxb_init_done(struct saa7146_dev* dev)
tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
/* set a default video standard */
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ saa7111a_call(mxb, core, s_gpio, 1);
+ saa7111a_call(mxb, core, s_std, std);
tuner_call(mxb, core, s_std, std);
- /* mute audio on tea6420s */
- tea6420_route_line(mxb, 6);
- tea6420_route_cd(mxb, 6);
-
/* switch to tuner-channel on tea6415c */
tea6415c_call(mxb, video, s_routing, 3, 17, 0);
@@ -320,9 +390,11 @@ static int mxb_init_done(struct saa7146_dev* dev)
/* the rest for mxb */
mxb->cur_input = 0;
+ mxb->cur_audinput = video_audio_connect[mxb->cur_input];
mxb->cur_mute = 1;
mxb->cur_mode = V4L2_TUNER_MODE_STEREO;
+ mxb_update_audmode(mxb);
/* check if the saa7740 (aka 'sound arena module') is present
on the mxb. if so, we must initialize it. due to lack of
@@ -385,69 +457,6 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
}
*/
-static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- int i;
-
- for (i = MAXCONTROLS - 1; i >= 0; i--) {
- if (mxb_controls[i].id == qc->id) {
- *qc = mxb_controls[i];
- DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id);
- return 0;
- }
- }
- return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
-}
-
-static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
- int i;
-
- for (i = MAXCONTROLS - 1; i >= 0; i--) {
- if (mxb_controls[i].id == vc->id)
- break;
- }
-
- if (i < 0)
- return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
-
- if (vc->id == V4L2_CID_AUDIO_MUTE) {
- vc->value = mxb->cur_mute;
- DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
- return 0;
- }
-
- DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value);
- return 0;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
- int i = 0;
-
- for (i = MAXCONTROLS - 1; i >= 0; i--) {
- if (mxb_controls[i].id == vc->id)
- break;
- }
-
- if (i < 0)
- return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
-
- if (vc->id == V4L2_CID_AUDIO_MUTE) {
- mxb->cur_mute = vc->value;
- /* switch the audio-source */
- tea6420_route_line(mxb, vc->value ? 6 :
- video_audio_connect[mxb->cur_input]);
- DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value);
- }
- return 0;
-}
-
static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
{
DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index);
@@ -519,9 +528,12 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0))
pr_err("VIDIOC_S_INPUT: could not address saa7111a\n");
+ mxb->cur_audinput = video_audio_connect[input];
/* switch the audio-source only if necessary */
if (0 == mxb->cur_mute)
- tea6420_route_line(mxb, video_audio_connect[input]);
+ tea6420_route(mxb, mxb->cur_audinput);
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
return 0;
}
@@ -563,17 +575,20 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
return call_all(dev, tuner, s_tuner, t);
}
+static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm)
+{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+ return call_all(dev, video, querystd, norm);
+}
+
static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
{
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct mxb *mxb = (struct mxb *)dev->ext_priv;
- if (mxb->cur_input) {
- DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
- mxb->cur_input);
+ if (f->tuner)
return -EINVAL;
- }
-
*f = mxb->cur_freq;
DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency);
@@ -592,17 +607,18 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency
if (V4L2_TUNER_ANALOG_TV != f->type)
return -EINVAL;
- if (mxb->cur_input) {
- DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n",
- mxb->cur_input);
- return -EINVAL;
- }
-
- mxb->cur_freq = *f;
DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency);
/* tune in desired frequency */
- tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+ tuner_call(mxb, tuner, s_frequency, f);
+ /* let the tuner subdev clamp the frequency to the tuner range */
+ tuner_call(mxb, tuner, g_frequency, f);
+ mxb->cur_freq = *f;
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+
+ if (mxb->cur_input)
+ return 0;
/* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
spin_lock(&dev->slock);
@@ -612,25 +628,40 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency
return 0;
}
+static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+ if (a->index >= MXB_AUDIOS)
+ return -EINVAL;
+ *a = mxb_audios[a->index];
+ return 0;
+}
+
static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
{
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
struct mxb *mxb = (struct mxb *)dev->ext_priv;
- if (a->index > MXB_INPUTS) {
- DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index);
- return -EINVAL;
- }
-
- DEB_EE("VIDIOC_G_AUDIO %d\n", a->index);
- memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
+ DEB_EE("VIDIOC_G_AUDIO\n");
+ *a = mxb_audios[mxb->cur_audinput];
return 0;
}
static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
{
+ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
DEB_D("VIDIOC_S_AUDIO %d\n", a->index);
- return 0;
+ if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) {
+ if (mxb->cur_audinput != a->index) {
+ mxb->cur_audinput = a->index;
+ tea6420_route(mxb, a->index);
+ if (mxb->cur_audinput == 0)
+ mxb_update_audmode(mxb);
+ }
+ return 0;
+ }
+ return -EINVAL;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -638,60 +669,31 @@ static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_regist
{
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- return call_all(dev, core, g_register, reg);
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (v4l2_chip_match_host(&reg->match)) {
+ reg->val = saa7146_read(dev, reg->reg);
+ reg->size = 4;
+ return 0;
+ }
+ call_all(dev, core, g_register, reg);
+ return 0;
}
static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
{
struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- return call_all(dev, core, s_register, reg);
-}
-#endif
-
-static long vidioc_default(struct file *file, void *fh, bool valid_prio,
- int cmd, void *arg)
-{
- struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
- struct mxb *mxb = (struct mxb *)dev->ext_priv;
-
- switch (cmd) {
- case MXB_S_AUDIO_CD:
- {
- int i = *(int *)arg;
-
- if (i < 0 || i >= MXB_AUDIOS) {
- DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i);
- return -EINVAL;
- }
-
- DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i);
-
- tea6420_route_cd(mxb, i);
- return 0;
- }
- case MXB_S_AUDIO_LINE:
- {
- int i = *(int *)arg;
-
- if (i < 0 || i >= MXB_AUDIOS) {
- DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n",
- i);
- return -EINVAL;
- }
-
- DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i);
- tea6420_route_line(mxb, i);
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (v4l2_chip_match_host(&reg->match)) {
+ saa7146_write(dev, reg->reg, reg->val);
+ reg->size = 4;
return 0;
}
- default:
-/*
- DEB2(pr_err("does not handle this ioctl\n"));
-*/
- return -ENOIOCTLCMD;
- }
- return 0;
+ return call_all(dev, core, s_register, reg);
}
+#endif
static struct saa7146_ext_vv vv_data;
@@ -709,23 +711,21 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data
}
mxb = (struct mxb *)dev->ext_priv;
- vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
- vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
- vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
- vv_data.ops.vidioc_enum_input = vidioc_enum_input;
- vv_data.ops.vidioc_g_input = vidioc_g_input;
- vv_data.ops.vidioc_s_input = vidioc_s_input;
- vv_data.ops.vidioc_g_tuner = vidioc_g_tuner;
- vv_data.ops.vidioc_s_tuner = vidioc_s_tuner;
- vv_data.ops.vidioc_g_frequency = vidioc_g_frequency;
- vv_data.ops.vidioc_s_frequency = vidioc_s_frequency;
- vv_data.ops.vidioc_g_audio = vidioc_g_audio;
- vv_data.ops.vidioc_s_audio = vidioc_s_audio;
+ vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input;
+ vv_data.vid_ops.vidioc_g_input = vidioc_g_input;
+ vv_data.vid_ops.vidioc_s_input = vidioc_s_input;
+ vv_data.vid_ops.vidioc_querystd = vidioc_querystd;
+ vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner;
+ vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner;
+ vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency;
+ vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency;
+ vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio;
+ vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio;
+ vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio;
#ifdef CONFIG_VIDEO_ADV_DEBUG
- vv_data.ops.vidioc_g_register = vidioc_g_register;
- vv_data.ops.vidioc_s_register = vidioc_s_register;
+ vv_data.vid_ops.vidioc_g_register = vidioc_g_register;
+ vv_data.vid_ops.vidioc_s_register = vidioc_s_register;
#endif
- vv_data.ops.vidioc_default = vidioc_default;
if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
ERR("cannot register capture v4l2 device. skipping.\n");
saa7146_vv_release(dev);
@@ -752,6 +752,9 @@ static int mxb_detach(struct saa7146_dev *dev)
DEB_EE("dev:%p\n", dev);
+ /* mute audio on tea6420s */
+ tea6420_route(mxb, 6);
+
saa7146_unregister_device(&mxb->video_dev,dev);
if (MXB_BOARD_CAN_DO_VBI(dev))
saa7146_unregister_device(&mxb->vbi_dev, dev);
@@ -773,20 +776,24 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
v4l2_std_id std = V4L2_STD_PAL_I;
DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n");
- /* set the 7146 gpio register -- I don't know what this does exactly */
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
- /* unset the 7111 gpio register -- I don't know what this does exactly */
saa7111a_call(mxb, core, s_gpio, 0);
- tuner_call(mxb, core, s_std, std);
+ saa7111a_call(mxb, core, s_std, std);
+ if (mxb->cur_input == 0)
+ tuner_call(mxb, core, s_std, std);
} else {
v4l2_std_id std = V4L2_STD_PAL_BG;
+ if (mxb->cur_input)
+ std = standard->id;
DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n");
- /* set the 7146 gpio register -- I don't know what this does exactly */
+ /* These two gpio calls set the GPIO pins that control the tda9820 */
saa7146_write(dev, GPIO_CTRL, 0x00404050);
- /* set the 7111 gpio register -- I don't know what this does exactly */
saa7111a_call(mxb, core, s_gpio, 1);
- tuner_call(mxb, core, s_std, std);
+ saa7111a_call(mxb, core, s_std, std);
+ if (mxb->cur_input == 0)
+ tuner_call(mxb, core, s_std, std);
}
return 0;
}
@@ -836,14 +843,14 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_ext_vv vv_data = {
.inputs = MXB_INPUTS,
- .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO,
.stds = &standard[0],
.num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
.std_callback = &std_callback,
};
static struct saa7146_extension extension = {
- .name = MXB_IDENTIFIER,
+ .name = "Multimedia eXtension Board",
.flags = SAA7146_USE_I2C_IRQ,
.pci_tbl = &pci_tbl[0],
diff --git a/drivers/media/video/mxb.h b/drivers/media/video/mxb.h
deleted file mode 100644
index 400a57ba62e..00000000000
--- a/drivers/media/video/mxb.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef __MXB__
-#define __MXB__
-
-#define BASE_VIDIOC_MXB 10
-
-#define MXB_S_AUDIO_CD _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+0, int)
-#define MXB_S_AUDIO_LINE _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+1, int)
-
-#define MXB_IDENTIFIER "Multimedia eXtension Board"
-
-#define MXB_AUDIOS 6
-
-/* these are the available audio sources, which can switched
- to the line- and cd-output individually */
-static struct v4l2_audio mxb_audios[MXB_AUDIOS] = {
- {
- .index = 0,
- .name = "Tuner",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 1,
- .name = "AUX1",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 2,
- .name = "AUX2",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 3,
- .name = "AUX3",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 4,
- .name = "Radio (X9)",
- .capability = V4L2_AUDCAP_STEREO,
- } , {
- .index = 5,
- .name = "CD-ROM (X10)",
- .capability = V4L2_AUDCAP_STEREO,
- }
-};
-#endif
diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c
index c20f5ecd679..c7e41145041 100644
--- a/drivers/media/video/omap1_camera.c
+++ b/drivers/media/video/omap1_camera.c
@@ -206,15 +206,10 @@ static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
- if (bytes_per_line < 0)
- return bytes_per_line;
-
- *size = bytes_per_line * icd->user_height;
+ *size = icd->sizeimage;
if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode))
*count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode);
@@ -256,15 +251,10 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq,
{
struct soc_camera_device *icd = vq->priv_data;
struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb);
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct omap1_cam_dev *pcdev = ici->priv;
int ret;
- if (bytes_per_line < 0)
- return bytes_per_line;
-
WARN_ON(!list_empty(&vb->queue));
BUG_ON(NULL == icd->current_fmt);
@@ -281,7 +271,7 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq,
vb->state = VIDEOBUF_NEEDS_INIT;
}
- vb->size = bytes_per_line * vb->height;
+ vb->size = icd->sizeimage;
if (vb->baddr && vb->bsize < vb->size) {
ret = -EINVAL;
@@ -999,6 +989,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_VYUY8_2X8,
@@ -1008,6 +999,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YUYV8_2X8,
@@ -1017,6 +1009,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YVYU8_2X8,
@@ -1026,6 +1019,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
@@ -1035,6 +1029,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
@@ -1044,6 +1039,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB565_2X8_BE,
@@ -1053,6 +1049,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB565_2X8_LE,
@@ -1062,6 +1059,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
},
};
diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c
index 3ea38a8def8..b5ae170de4a 100644
--- a/drivers/media/video/omap24xxcam-dma.c
+++ b/drivers/media/video/omap24xxcam-dma.c
@@ -38,7 +38,7 @@
*/
/* Ack all interrupt on CSR and IRQSTATUS_L0 */
-static void omap24xxcam_dmahw_ack_all(unsigned long base)
+static void omap24xxcam_dmahw_ack_all(void __iomem *base)
{
u32 csr;
int i;
@@ -52,7 +52,7 @@ static void omap24xxcam_dmahw_ack_all(unsigned long base)
}
/* Ack dmach on CSR and IRQSTATUS_L0 */
-static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
+static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach)
{
u32 csr;
@@ -65,12 +65,12 @@ static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach)
return csr;
}
-static int omap24xxcam_dmahw_running(unsigned long base, int dmach)
+static int omap24xxcam_dmahw_running(void __iomem *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,
+static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach,
dma_addr_t start, u32 len)
{
omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
@@ -112,7 +112,7 @@ static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach,
| CAMDMA_CICR_DROP_IE);
}
-static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
+static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach)
{
omap24xxcam_reg_out(base, CAMDMA_CCR(dmach),
CAMDMA_CCR_SEL_SRC_DST_SYNC
@@ -124,7 +124,7 @@ static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach)
| CAMDMA_CCR_SYNCHRO_CAMERA);
}
-static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
+static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach,
int free_dmach)
{
int prev_dmach, ch;
@@ -160,7 +160,7 @@ static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach,
* 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)
+static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach)
{
/* mask all interrupts from this channel */
omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0);
@@ -171,7 +171,7 @@ static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach)
omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE);
}
-static void omap24xxcam_dmahw_init(unsigned long base)
+static void omap24xxcam_dmahw_init(void __iomem *base)
{
omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG,
CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY
@@ -362,7 +362,7 @@ void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma)
}
static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma,
- unsigned long base)
+ void __iomem *base)
{
int ch;
@@ -577,7 +577,7 @@ void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma)
}
void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma,
- unsigned long base,
+ void __iomem *base,
void (*reset_callback)(unsigned long data),
unsigned long reset_callback_data)
{
diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
index 7d386414436..e5015b0d550 100644
--- a/drivers/media/video/omap24xxcam.c
+++ b/drivers/media/video/omap24xxcam.c
@@ -1776,8 +1776,7 @@ static int __devinit omap24xxcam_probe(struct platform_device *pdev)
cam->mmio_size = resource_size(mem);
/* map the region */
- cam->mmio_base = (unsigned long)
- ioremap_nocache(cam->mmio_base_phys, cam->mmio_size);
+ cam->mmio_base = 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;
diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h
index 2ce67f5a48d..d59727afe89 100644
--- a/drivers/media/video/omap24xxcam.h
+++ b/drivers/media/video/omap24xxcam.h
@@ -429,7 +429,7 @@ struct sgdma_state {
struct omap24xxcam_dma {
spinlock_t lock; /* Lock for the whole structure. */
- unsigned long base; /* base address for dma controller */
+ void __iomem *base; /* base address for dma controller */
/* While dma_stop!=0, an attempt to start a new DMA transfer will
* fail.
@@ -491,7 +491,7 @@ struct omap24xxcam_device {
/*** hardware resources ***/
unsigned int irq;
- unsigned long mmio_base;
+ void __iomem *mmio_base;
unsigned long mmio_base_phys;
unsigned long mmio_size;
@@ -544,22 +544,22 @@ struct omap24xxcam_fh {
*
*/
-static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset)
+static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset)
{
return readl(base + offset);
}
-static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset,
+static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset,
u32 val)
{
writel(val, base + offset);
return val;
}
-static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset,
+static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset,
u32 val, u32 mask)
{
- u32 addr = base + offset;
+ u32 __iomem *addr = base + offset;
u32 new_val = (readl(addr) & ~mask) | (val & mask);
writel(new_val, addr);
@@ -585,7 +585,7 @@ int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma,
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 __iomem *base,
void (*reset_callback)(unsigned long data),
unsigned long reset_callback_data);
void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma);
diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c
index 12d5f923e1d..1c347633e66 100644
--- a/drivers/media/video/omap3isp/isp.c
+++ b/drivers/media/video/omap3isp/isp.c
@@ -329,19 +329,6 @@ void omap3isp_configure_bridge(struct isp_device *isp,
isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL);
}
-/**
- * isp_set_pixel_clock - Configures the ISP pixel clock
- * @isp: OMAP3 ISP device
- * @pixelclk: Average pixel clock in Hz
- *
- * Set the average pixel clock required by the sensor. The ISP will use the
- * lowest possible memory bandwidth settings compatible with the clock.
- **/
-static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk)
-{
- isp->isp_ccdc.vpcfg.pixelclk = pixelclk;
-}
-
void omap3isp_hist_dma_done(struct isp_device *isp)
{
if (omap3isp_ccdc_busy(&isp->isp_ccdc) ||
@@ -739,6 +726,17 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
unsigned long flags;
int ret;
+ /* If the preview engine crashed it might not respond to read/write
+ * operations on the L4 bus. This would result in a bus fault and a
+ * kernel oops. Refuse to start streaming in that case. This check must
+ * be performed before the loop below to avoid starting entities if the
+ * pipeline won't start anyway (those entities would then likely fail to
+ * stop, making the problem worse).
+ */
+ if ((pipe->entities & isp->crashed) &
+ (1U << isp->isp_prev.subdev.entity.id))
+ return -EIO;
+
spin_lock_irqsave(&pipe->lock, flags);
pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT);
spin_unlock_irqrestore(&pipe->lock, flags);
@@ -774,14 +772,6 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe,
}
}
- /* Frame number propagation. In continuous streaming mode the number
- * is incremented in the frame start ISR. In mem-to-mem mode
- * singleshot is used and frame start IRQs are not available.
- * Thus we have to increment the number here.
- */
- if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT)
- atomic_inc(&pipe->frame_number);
-
return 0;
}
@@ -879,13 +869,15 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe)
if (ret) {
dev_info(isp->dev, "Unable to stop %s\n", subdev->name);
+ /* If the entity failed to stopped, assume it has
+ * crashed. Mark it as such, the ISP will be reset when
+ * applications will release it.
+ */
+ isp->crashed |= 1U << subdev->entity.id;
failure = -ETIMEDOUT;
}
}
- if (failure < 0)
- isp->needs_reset = true;
-
return failure;
}
@@ -1069,6 +1061,7 @@ static int isp_reset(struct isp_device *isp)
udelay(1);
}
+ isp->crashed = 0;
return 0;
}
@@ -1495,11 +1488,13 @@ void omap3isp_put(struct isp_device *isp)
BUG_ON(isp->ref_count == 0);
if (--isp->ref_count == 0) {
isp_disable_interrupts(isp);
- isp_save_ctx(isp);
- if (isp->needs_reset) {
+ if (isp->domain)
+ isp_save_ctx(isp);
+ /* Reset the ISP if an entity has failed to stop. This is the
+ * only way to recover from such conditions.
+ */
+ if (isp->crashed)
isp_reset(isp);
- isp->needs_reset = false;
- }
isp_disable_clocks(isp);
}
mutex_unlock(&isp->isp_mutex);
@@ -1970,7 +1965,7 @@ error_csiphy:
*
* Always returns 0.
*/
-static int isp_remove(struct platform_device *pdev)
+static int __devexit isp_remove(struct platform_device *pdev)
{
struct isp_device *isp = platform_get_drvdata(pdev);
int i;
@@ -1981,6 +1976,7 @@ static int isp_remove(struct platform_device *pdev)
omap3isp_get(isp);
iommu_detach_device(isp->domain, &pdev->dev);
iommu_domain_free(isp->domain);
+ isp->domain = NULL;
omap3isp_put(isp);
free_irq(isp->irq_num, isp);
@@ -2050,7 +2046,7 @@ static int isp_map_mem_resource(struct platform_device *pdev,
* -EINVAL if couldn't install ISR,
* or clk_get return error value.
*/
-static int isp_probe(struct platform_device *pdev)
+static int __devinit isp_probe(struct platform_device *pdev)
{
struct isp_platform_data *pdata = pdev->dev.platform_data;
struct isp_device *isp;
@@ -2068,7 +2064,6 @@ static int isp_probe(struct platform_device *pdev)
isp->autoidle = autoidle;
isp->platform_cb.set_xclk = isp_set_xclk;
- isp->platform_cb.set_pixel_clock = isp_set_pixel_clock;
mutex_init(&isp->isp_mutex);
spin_lock_init(&isp->stat_lock);
@@ -2218,7 +2213,7 @@ MODULE_DEVICE_TABLE(platform, omap3isp_id_table);
static struct platform_driver omap3isp_driver = {
.probe = isp_probe,
- .remove = isp_remove,
+ .remove = __devexit_p(isp_remove),
.id_table = omap3isp_id_table,
.driver = {
.owner = THIS_MODULE,
diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h
index d96603eb0d1..fc7af3e32ef 100644
--- a/drivers/media/video/omap3isp/isp.h
+++ b/drivers/media/video/omap3isp/isp.h
@@ -129,7 +129,6 @@ struct isp_platform_callback {
int (*csiphy_config)(struct isp_csiphy *phy,
struct isp_csiphy_dphy_cfg *dphy,
struct isp_csiphy_lanes_cfg *lanes);
- void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk);
};
/*
@@ -145,6 +144,7 @@ struct isp_platform_callback {
* @raw_dmamask: Raw DMA mask
* @stat_lock: Spinlock for handling statistics
* @isp_mutex: Mutex for serializing requests to ISP.
+ * @crashed: Bitmask of crashed entities (indexed by entity ID)
* @has_context: Context has been saved at least once and can be restored.
* @ref_count: Reference count for handling multiple ISP requests.
* @cam_ick: Pointer to camera interface clock structure.
@@ -184,7 +184,7 @@ struct isp_device {
/* ISP Obj */
spinlock_t stat_lock; /* common lock for statistic drivers */
struct mutex isp_mutex; /* For handling ref_count field */
- bool needs_reset;
+ u32 crashed;
int has_context;
int ref_count;
unsigned int autoidle;
@@ -237,10 +237,6 @@ void omap3isp_configure_bridge(struct isp_device *isp,
const struct isp_parallel_platform_data *pdata,
unsigned int shift);
-#define ISP_XCLK_NONE 0
-#define ISP_XCLK_A 1
-#define ISP_XCLK_B 2
-
struct isp_device *omap3isp_get(struct isp_device *isp);
void omap3isp_put(struct isp_device *isp);
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c
index eaabc27f0fa..7e32331b60f 100644
--- a/drivers/media/video/omap3isp/ispccdc.c
+++ b/drivers/media/video/omap3isp/ispccdc.c
@@ -38,6 +38,9 @@
#include "ispreg.h"
#include "ispccdc.h"
+#define CCDC_MIN_WIDTH 32
+#define CCDC_MIN_HEIGHT 32
+
static struct v4l2_mbus_framefmt *
__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
unsigned int pad, enum v4l2_subdev_format_whence which);
@@ -836,8 +839,8 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc)
if (pipe->input)
div = DIV_ROUND_UP(l3_ick, pipe->max_rate);
- else if (ccdc->vpcfg.pixelclk)
- div = l3_ick / ccdc->vpcfg.pixelclk;
+ else if (pipe->external_rate)
+ div = l3_ick / pipe->external_rate;
div = clamp(div, 2U, max_div);
fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT;
@@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
struct isp_parallel_platform_data *pdata = NULL;
struct v4l2_subdev *sensor;
struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
const struct isp_format_info *fmt_info;
struct v4l2_subdev_format fmt_src;
unsigned int depth_out;
@@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc)
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT);
/* CCDC_PAD_SOURCE_OF */
- format = &ccdc->formats[CCDC_PAD_SOURCE_OF];
+ crop = &ccdc->crop;
- isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
- ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
+ isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) |
+ ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT),
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO);
- isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT,
+ isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT,
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START);
- isp_reg_writel(isp, (format->height - 1)
+ isp_reg_writel(isp, (crop->height - 1)
<< ISPCCDC_VERT_LINES_NLV_SHIFT,
OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES);
@@ -1410,6 +1414,9 @@ static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc)
struct video_device *vdev = ccdc->subdev.devnode;
struct v4l2_event event;
+ /* Frame number propagation */
+ atomic_inc(&pipe->frame_number);
+
memset(&event, 0, sizeof(event));
event.type = V4L2_EVENT_FRAME_SYNC;
event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number);
@@ -1703,7 +1710,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
if (sub->id != 0)
return -EINVAL;
- return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS);
+ return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL);
}
static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
@@ -1790,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
return &ccdc->formats[pad];
}
+static struct v4l2_rect *
+__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF);
+ else
+ return &ccdc->crop;
+}
+
/*
* ccdc_try_format - Try video format on a pad
* @ccdc: ISP CCDC device
@@ -1806,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
const struct isp_format_info *info;
unsigned int width = fmt->width;
unsigned int height = fmt->height;
+ struct v4l2_rect *crop;
unsigned int i;
switch (pad) {
@@ -1831,14 +1849,10 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which);
memcpy(fmt, format, sizeof(*fmt));
- /* The data formatter truncates the number of horizontal output
- * pixels to a multiple of 16. To avoid clipping data, allow
- * callers to request an output size bigger than the input size
- * up to the nearest multiple of 16.
- */
- fmt->width = clamp_t(u32, width, 32, fmt->width + 15);
- fmt->width &= ~15;
- fmt->height = clamp_t(u32, height, 32, fmt->height);
+ /* Hardcode the output size to the crop rectangle size. */
+ crop = __ccdc_get_crop(ccdc, fh, which);
+ fmt->width = crop->width;
+ fmt->height = crop->height;
break;
case CCDC_PAD_SOURCE_VP:
@@ -1866,6 +1880,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh,
}
/*
+ * ccdc_try_crop - Validate a crop rectangle
+ * @ccdc: ISP CCDC device
+ * @sink: format on the sink pad
+ * @crop: crop rectangle to be validated
+ */
+static void ccdc_try_crop(struct isp_ccdc_device *ccdc,
+ const struct v4l2_mbus_framefmt *sink,
+ struct v4l2_rect *crop)
+{
+ const struct isp_format_info *info;
+ unsigned int max_width;
+
+ /* For Bayer formats, restrict left/top and width/height to even values
+ * to keep the Bayer pattern.
+ */
+ info = omap3isp_video_format_info(sink->code);
+ if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
+ crop->left &= ~1;
+ crop->top &= ~1;
+ }
+
+ crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH);
+ crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT);
+
+ /* The data formatter truncates the number of horizontal output pixels
+ * to a multiple of 16. To avoid clipping data, allow callers to request
+ * an output size bigger than the input size up to the nearest multiple
+ * of 16.
+ */
+ max_width = (sink->width - crop->left + 15) & ~15;
+ crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width)
+ & ~15;
+ crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT,
+ sink->height - crop->top);
+
+ /* Odd width/height values don't make sense for Bayer formats. */
+ if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) {
+ crop->width &= ~1;
+ crop->height &= ~1;
+ }
+}
+
+/*
* ccdc_enum_mbus_code - Handle pixel format enumeration
* @sd : pointer to v4l2 subdev structure
* @fh : V4L2 subdev file handle
@@ -1937,6 +1994,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
}
/*
+ * ccdc_get_selection - Retrieve a selection rectangle on a pad
+ * @sd: ISP CCDC V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangles are the crop rectangles on the output formatter
+ * source pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (sel->pad != CCDC_PAD_SOURCE_OF)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = INT_MAX;
+ sel->r.height = INT_MAX;
+
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
+ ccdc_try_crop(ccdc, format, &sel->r);
+ break;
+
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * ccdc_set_selection - Set a selection rectangle on a pad
+ * @sd: ISP CCDC V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangle is the actual crop rectangle on the output
+ * formatter source pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+ sel->pad != CCDC_PAD_SOURCE_OF)
+ return -EINVAL;
+
+ /* The crop rectangle can't be changed while streaming. */
+ if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED)
+ return -EBUSY;
+
+ /* Modifying the crop rectangle always changes the format on the source
+ * pad. If the KEEP_CONFIG flag is set, just return the current crop
+ * rectangle.
+ */
+ if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) {
+ sel->r = *__ccdc_get_crop(ccdc, fh, sel->which);
+ return 0;
+ }
+
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which);
+ ccdc_try_crop(ccdc, format, &sel->r);
+ *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r;
+
+ /* Update the source format. */
+ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which);
+ ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which);
+
+ return 0;
+}
+
+/*
* ccdc_get_format - Retrieve the video format on a pad
* @sd : ISP CCDC V4L2 subdevice
* @fh : V4L2 subdev file handle
@@ -1973,6 +2117,7 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
{
struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *crop;
format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which);
if (format == NULL)
@@ -1983,6 +2128,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
/* Propagate the format from sink to source */
if (fmt->pad == CCDC_PAD_SINK) {
+ /* Reset the crop rectangle. */
+ crop = __ccdc_get_crop(ccdc, fh, fmt->which);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+
+ ccdc_try_crop(ccdc, &fmt->format, crop);
+
+ /* Update the source formats. */
format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF,
fmt->which);
*format = fmt->format;
@@ -2000,6 +2155,69 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
}
/*
+ * Decide whether desired output pixel code can be obtained with
+ * the lane shifter by shifting the input pixel code.
+ * @in: input pixelcode to shifter
+ * @out: output pixelcode from shifter
+ * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
+ *
+ * return true if the combination is possible
+ * return false otherwise
+ */
+static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in,
+ enum v4l2_mbus_pixelcode out,
+ unsigned int additional_shift)
+{
+ const struct isp_format_info *in_info, *out_info;
+
+ if (in == out)
+ return true;
+
+ in_info = omap3isp_video_format_info(in);
+ out_info = omap3isp_video_format_info(out);
+
+ if ((in_info->flavor == 0) || (out_info->flavor == 0))
+ return false;
+
+ if (in_info->flavor != out_info->flavor)
+ return false;
+
+ return in_info->bpp - out_info->bpp + additional_shift <= 6;
+}
+
+static int ccdc_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd);
+ unsigned long parallel_shift;
+
+ /* Check if the two ends match */
+ if (source_fmt->format.width != sink_fmt->format.width ||
+ source_fmt->format.height != sink_fmt->format.height)
+ return -EPIPE;
+
+ /* We've got a parallel sensor here. */
+ if (ccdc->input == CCDC_INPUT_PARALLEL) {
+ struct isp_parallel_platform_data *pdata =
+ &((struct isp_v4l2_subdevs_group *)
+ media_entity_to_v4l2_subdev(link->source->entity)
+ ->host_priv)->bus.parallel;
+ parallel_shift = pdata->data_lane_shift * 2;
+ } else {
+ parallel_shift = 0;
+ }
+
+ /* Lane shifter may be used to drop bits on CCDC sink pad */
+ if (!ccdc_is_shiftable(source_fmt->format.code,
+ sink_fmt->format.code, parallel_shift))
+ return -EPIPE;
+
+ return 0;
+}
+
+/*
* ccdc_init_formats - Initialize formats on all pads
* @sd: ISP CCDC V4L2 subdevice
* @fh: V4L2 subdev file handle
@@ -2041,6 +2259,9 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = {
.enum_frame_size = ccdc_enum_frame_size,
.get_fmt = ccdc_get_format,
.set_fmt = ccdc_set_format,
+ .get_selection = ccdc_get_selection,
+ .set_selection = ccdc_set_selection,
+ .link_validate = ccdc_link_validate,
};
/* V4L2 subdev operations */
@@ -2150,6 +2371,7 @@ static int ccdc_link_setup(struct media_entity *entity,
/* media operations */
static const struct media_entity_operations ccdc_media_ops = {
.link_setup = ccdc_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
};
void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc)
@@ -2276,8 +2498,6 @@ int omap3isp_ccdc_init(struct isp_device *isp)
ccdc->clamp.oblen = 0;
ccdc->clamp.dcsubval = 0;
- ccdc->vpcfg.pixelclk = 0;
-
ccdc->update = OMAP3ISP_CCDC_BLCLAMP;
ccdc_apply_controls(ccdc);
diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h
index 6d0264bab75..890f6b3a68f 100644
--- a/drivers/media/video/omap3isp/ispccdc.h
+++ b/drivers/media/video/omap3isp/ispccdc.h
@@ -80,14 +80,6 @@ struct ispccdc_syncif {
u8 bt_r656_en;
};
-/*
- * struct ispccdc_vp - Structure for Video Port parameters
- * @pixelclk: Input pixel clock in Hz
- */
-struct ispccdc_vp {
- unsigned int pixelclk;
-};
-
enum ispccdc_lsc_state {
LSC_STATE_STOPPED = 0,
LSC_STATE_STOPPING = 1,
@@ -147,6 +139,7 @@ struct ispccdc_lsc {
* @subdev: V4L2 subdevice
* @pads: Sink and source media entity pads
* @formats: Active video formats
+ * @crop: Active crop rectangle on the OF source pad
* @input: Active input
* @output: Active outputs
* @video_out: Output video node
@@ -161,7 +154,6 @@ struct ispccdc_lsc {
* @update: Bitmask of controls to update during the next interrupt
* @shadow_update: Controls update in progress by userspace
* @syncif: Interface synchronization configuration
- * @vpcfg: Video port configuration
* @underrun: A buffer underrun occurred and a new buffer has been queued
* @state: Streaming state
* @lock: Serializes shadow_update with interrupt handler
@@ -173,6 +165,7 @@ struct isp_ccdc_device {
struct v4l2_subdev subdev;
struct media_pad pads[CCDC_PADS_NUM];
struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM];
+ struct v4l2_rect crop;
enum ccdc_input_entity input;
unsigned int output;
@@ -190,7 +183,6 @@ struct isp_ccdc_device {
unsigned int shadow_update;
struct ispccdc_syncif syncif;
- struct ispccdc_vp vpcfg;
unsigned int underrun:1;
enum isp_pipeline_stream_state state;
diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c
index 70ddbf35b22..85f0de85f37 100644
--- a/drivers/media/video/omap3isp/ispccp2.c
+++ b/drivers/media/video/omap3isp/ispccp2.c
@@ -161,7 +161,6 @@ static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2)
static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
{
struct isp_device *isp = to_isp_device(ccp2);
- struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity);
int i;
if (enable && ccp2->vdds_csib)
@@ -178,19 +177,6 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable)
ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN,
enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0);
- /* For frame count propagation */
- if (pipe->do_propagation) {
- /* We may want the Frame Start IRQ from LC0 */
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2,
- ISPCCP2_LC01_IRQENABLE,
- ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2,
- ISPCCP2_LC01_IRQENABLE,
- ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ);
- }
-
if (!enable && ccp2->vdds_csib)
regulator_disable(ccp2->vdds_csib);
}
@@ -350,7 +336,6 @@ static void ccp2_lcx_config(struct isp_ccp2_device *ccp2,
ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ |
- ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ |
ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ;
@@ -613,14 +598,6 @@ void omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2)
if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping))
return;
- /* Frame number propagation */
- if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) {
- struct isp_pipeline *pipe =
- to_isp_pipeline(&ccp2->subdev.entity);
- if (pipe->do_propagation)
- atomic_inc(&pipe->frame_number);
- }
-
/* Handle queued buffers on frame end interrupts */
if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ)
ccp2_isr_buffer(ccp2);
@@ -1021,6 +998,7 @@ static int ccp2_link_setup(struct media_entity *entity,
/* media operations */
static const struct media_entity_operations ccp2_media_ops = {
.link_setup = ccp2_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
};
/*
diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c
index fcb5168996a..a1724362b6d 100644
--- a/drivers/media/video/omap3isp/ispcsi2.c
+++ b/drivers/media/video/omap3isp/ispcsi2.c
@@ -378,21 +378,17 @@ static void csi2_timing_config(struct isp_device *isp,
static void csi2_irq_ctx_set(struct isp_device *isp,
struct isp_csi2_device *csi2, int enable)
{
- u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ;
int i;
- if (csi2->use_fs_irq)
- reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ;
-
for (i = 0; i < 8; i++) {
- isp_reg_writel(isp, reg, csi2->regs1,
+ isp_reg_writel(isp, ISPCSI2_CTX_IRQSTATUS_FE_IRQ, csi2->regs1,
ISPCSI2_CTX_IRQSTATUS(i));
if (enable)
isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
- reg);
+ ISPCSI2_CTX_IRQSTATUS_FE_IRQ);
else
isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i),
- reg);
+ ISPCSI2_CTX_IRQSTATUS_FE_IRQ);
}
}
@@ -690,14 +686,6 @@ static void csi2_isr_ctx(struct isp_csi2_device *csi2,
status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n));
- /* Propagate frame number */
- if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) {
- struct isp_pipeline *pipe =
- to_isp_pipeline(&csi2->subdev.entity);
- if (pipe->do_propagation)
- atomic_inc(&pipe->frame_number);
- }
-
if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ))
return;
@@ -1047,14 +1035,12 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable)
{
struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd);
struct isp_device *isp = csi2->isp;
- struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity);
struct isp_video *video_out = &csi2->video_out;
switch (enable) {
case ISP_PIPELINE_STREAM_CONTINUOUS:
if (omap3isp_csiphy_acquire(csi2->phy) < 0)
return -ENODEV;
- csi2->use_fs_irq = pipe->do_propagation;
if (csi2->output & CSI2_OUTPUT_MEMORY)
omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE);
csi2_configure(csi2);
@@ -1181,6 +1167,7 @@ static int csi2_link_setup(struct media_entity *entity,
/* media operations */
static const struct media_entity_operations csi2_media_ops = {
.link_setup = csi2_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
};
void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2)
diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/video/omap3isp/ispcsi2.h
index 885ad79a767..c57729b7e86 100644
--- a/drivers/media/video/omap3isp/ispcsi2.h
+++ b/drivers/media/video/omap3isp/ispcsi2.h
@@ -145,7 +145,6 @@ struct isp_csi2_device {
u32 output; /* output to CCDC, memory or both? */
bool dpcm_decompress;
unsigned int frame_skip;
- bool use_fs_irq;
struct isp_csiphy *phy;
struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1];
diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c
index 5be37ce7d0c..348f67ebbbc 100644
--- a/drivers/media/video/omap3isp/ispcsiphy.c
+++ b/drivers/media/video/omap3isp/ispcsiphy.c
@@ -186,7 +186,9 @@ int omap3isp_csiphy_acquire(struct isp_csiphy *phy)
if (rval < 0)
goto done;
- omap3isp_csi2_reset(phy->csi2);
+ rval = omap3isp_csi2_reset(phy->csi2);
+ if (rval < 0)
+ goto done;
csiphy_dphy_config(phy);
csiphy_lanes_config(phy);
diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/video/omap3isp/ispcsiphy.h
index 9596dc6830a..e93a661e65d 100644
--- a/drivers/media/video/omap3isp/ispcsiphy.h
+++ b/drivers/media/video/omap3isp/ispcsiphy.h
@@ -27,22 +27,11 @@
#ifndef OMAP3_ISP_CSI_PHY_H
#define OMAP3_ISP_CSI_PHY_H
+#include <media/omap3isp.h>
+
struct isp_csi2_device;
struct regulator;
-struct csiphy_lane {
- u8 pos;
- u8 pol;
-};
-
-#define ISP_CSIPHY2_NUM_DATA_LANES 2
-#define ISP_CSIPHY1_NUM_DATA_LANES 1
-
-struct isp_csiphy_lanes_cfg {
- struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES];
- struct csiphy_lane clk;
-};
-
struct isp_csiphy_dphy_cfg {
u8 ths_term;
u8 ths_settle;
diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c
index 6d0fb2c8c26..8a4935ecc65 100644
--- a/drivers/media/video/omap3isp/isppreview.c
+++ b/drivers/media/video/omap3isp/isppreview.c
@@ -441,23 +441,6 @@ preview_enable_dcor(struct isp_prev_device *prev, u8 enable)
}
/*
- * preview_enable_cfa - Enable/Disable the CFA Interpolation.
- * @enable: 1 - Enables the CFA.
- */
-static void
-preview_enable_cfa(struct isp_prev_device *prev, u8 enable)
-{
- struct isp_device *isp = to_isp_device(prev);
-
- if (enable)
- isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_CFAEN);
- else
- isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
- ISPPRV_PCR_CFAEN);
-}
-
-/*
* preview_enable_gammabypass - Enables/Disables the GammaByPass
* @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB.
* 0 - Goes through Gamma Correction. input and output is 10bit.
@@ -608,12 +591,12 @@ preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb)
}
/*
- * Configures the RGB-YCbYCr conversion matrix
+ * Configures the color space conversion (RGB toYCbYCr) matrix
* @prev_csc: Structure containing the RGB to YCbYCr matrix and the
* YCbCr offset.
*/
static void
-preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc)
+preview_config_csc(struct isp_prev_device *prev, const void *prev_csc)
{
struct isp_device *isp = to_isp_device(prev);
const struct omap3isp_prev_csc *csc = prev_csc;
@@ -649,12 +632,18 @@ preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc)
static void
preview_update_contrast(struct isp_prev_device *prev, u8 contrast)
{
- struct prev_params *params = &prev->params;
+ struct prev_params *params;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ params = (prev->params.active & OMAP3ISP_PREV_CONTRAST)
+ ? &prev->params.params[0] : &prev->params.params[1];
if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) {
params->contrast = contrast * ISPPRV_CONTRAST_UNITS;
- prev->update |= PREV_CONTRAST;
+ params->update |= OMAP3ISP_PREV_CONTRAST;
}
+ spin_unlock_irqrestore(&prev->params.lock, flags);
}
/*
@@ -681,12 +670,18 @@ preview_config_contrast(struct isp_prev_device *prev, const void *params)
static void
preview_update_brightness(struct isp_prev_device *prev, u8 brightness)
{
- struct prev_params *params = &prev->params;
+ struct prev_params *params;
+ unsigned long flags;
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ params = (prev->params.active & OMAP3ISP_PREV_BRIGHTNESS)
+ ? &prev->params.params[0] : &prev->params.params[1];
if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) {
params->brightness = brightness * ISPPRV_BRIGHT_UNITS;
- prev->update |= PREV_BRIGHTNESS;
+ params->update |= OMAP3ISP_PREV_BRIGHTNESS;
}
+ spin_unlock_irqrestore(&prev->params.lock, flags);
}
/*
@@ -721,159 +716,188 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit)
OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC);
}
+static u32
+preview_params_lock(struct isp_prev_device *prev, u32 update, bool shadow)
+{
+ u32 active = prev->params.active;
+
+ if (shadow) {
+ /* Mark all shadow parameters we are going to touch as busy. */
+ prev->params.params[0].busy |= ~active & update;
+ prev->params.params[1].busy |= active & update;
+ } else {
+ /* Mark all active parameters we are going to touch as busy. */
+ update = (prev->params.params[0].update & active)
+ | (prev->params.params[1].update & ~active);
+
+ prev->params.params[0].busy |= active & update;
+ prev->params.params[1].busy |= ~active & update;
+ }
+
+ return update;
+}
+
+static void
+preview_params_unlock(struct isp_prev_device *prev, u32 update, bool shadow)
+{
+ u32 active = prev->params.active;
+
+ if (shadow) {
+ /* Set the update flag for shadow parameters that have been
+ * updated and clear the busy flag for all shadow parameters.
+ */
+ prev->params.params[0].update |= (~active & update);
+ prev->params.params[1].update |= (active & update);
+ prev->params.params[0].busy &= active;
+ prev->params.params[1].busy &= ~active;
+ } else {
+ /* Clear the update flag for active parameters that have been
+ * applied and the busy flag for all active parameters.
+ */
+ prev->params.params[0].update &= ~(active & update);
+ prev->params.params[1].update &= ~(~active & update);
+ prev->params.params[0].busy &= ~active;
+ prev->params.params[1].busy &= active;
+ }
+}
+
+static void preview_params_switch(struct isp_prev_device *prev)
+{
+ u32 to_switch;
+
+ /* Switch active parameters with updated shadow parameters when the
+ * shadow parameter has been updated and neither the active not the
+ * shadow parameter is busy.
+ */
+ to_switch = (prev->params.params[0].update & ~prev->params.active)
+ | (prev->params.params[1].update & prev->params.active);
+ to_switch &= ~(prev->params.params[0].busy |
+ prev->params.params[1].busy);
+ if (to_switch == 0)
+ return;
+
+ prev->params.active ^= to_switch;
+
+ /* Remove the update flag for the shadow copy of parameters we have
+ * switched.
+ */
+ prev->params.params[0].update &= ~(~prev->params.active & to_switch);
+ prev->params.params[1].update &= ~(prev->params.active & to_switch);
+}
+
/* preview parameters update structure */
struct preview_update {
- int cfg_bit;
- int feature_bit;
void (*config)(struct isp_prev_device *, const void *);
void (*enable)(struct isp_prev_device *, u8);
+ unsigned int param_offset;
+ unsigned int param_size;
+ unsigned int config_offset;
+ bool skip;
};
-static struct preview_update update_attrs[] = {
- {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE,
+/* Keep the array indexed by the OMAP3ISP_PREV_* bit number. */
+static const struct preview_update update_attrs[] = {
+ /* OMAP3ISP_PREV_LUMAENH */ {
preview_config_luma_enhancement,
- preview_enable_luma_enhancement},
- {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW,
+ preview_enable_luma_enhancement,
+ offsetof(struct prev_params, luma),
+ FIELD_SIZEOF(struct prev_params, luma),
+ offsetof(struct omap3isp_prev_update_config, luma),
+ }, /* OMAP3ISP_PREV_INVALAW */ {
NULL,
- preview_enable_invalaw},
- {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER,
+ preview_enable_invalaw,
+ }, /* OMAP3ISP_PREV_HRZ_MED */ {
preview_config_hmed,
- preview_enable_hmed},
- {OMAP3ISP_PREV_CFA, PREV_CFA,
+ preview_enable_hmed,
+ offsetof(struct prev_params, hmed),
+ FIELD_SIZEOF(struct prev_params, hmed),
+ offsetof(struct omap3isp_prev_update_config, hmed),
+ }, /* OMAP3ISP_PREV_CFA */ {
preview_config_cfa,
- preview_enable_cfa},
- {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS,
+ NULL,
+ offsetof(struct prev_params, cfa),
+ FIELD_SIZEOF(struct prev_params, cfa),
+ offsetof(struct omap3isp_prev_update_config, cfa),
+ }, /* OMAP3ISP_PREV_CHROMA_SUPP */ {
preview_config_chroma_suppression,
- preview_enable_chroma_suppression},
- {OMAP3ISP_PREV_WB, PREV_WB,
+ preview_enable_chroma_suppression,
+ offsetof(struct prev_params, csup),
+ FIELD_SIZEOF(struct prev_params, csup),
+ offsetof(struct omap3isp_prev_update_config, csup),
+ }, /* OMAP3ISP_PREV_WB */ {
preview_config_whitebalance,
- NULL},
- {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ,
+ NULL,
+ offsetof(struct prev_params, wbal),
+ FIELD_SIZEOF(struct prev_params, wbal),
+ offsetof(struct omap3isp_prev_update_config, wbal),
+ }, /* OMAP3ISP_PREV_BLKADJ */ {
preview_config_blkadj,
- NULL},
- {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB,
+ NULL,
+ offsetof(struct prev_params, blkadj),
+ FIELD_SIZEOF(struct prev_params, blkadj),
+ offsetof(struct omap3isp_prev_update_config, blkadj),
+ }, /* OMAP3ISP_PREV_RGB2RGB */ {
preview_config_rgb_blending,
- NULL},
- {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV,
- preview_config_rgb_to_ycbcr,
- NULL},
- {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS,
+ NULL,
+ offsetof(struct prev_params, rgb2rgb),
+ FIELD_SIZEOF(struct prev_params, rgb2rgb),
+ offsetof(struct omap3isp_prev_update_config, rgb2rgb),
+ }, /* OMAP3ISP_PREV_COLOR_CONV */ {
+ preview_config_csc,
+ NULL,
+ offsetof(struct prev_params, csc),
+ FIELD_SIZEOF(struct prev_params, csc),
+ offsetof(struct omap3isp_prev_update_config, csc),
+ }, /* OMAP3ISP_PREV_YC_LIMIT */ {
preview_config_yc_range,
- NULL},
- {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR,
+ NULL,
+ offsetof(struct prev_params, yclimit),
+ FIELD_SIZEOF(struct prev_params, yclimit),
+ offsetof(struct omap3isp_prev_update_config, yclimit),
+ }, /* OMAP3ISP_PREV_DEFECT_COR */ {
preview_config_dcor,
- preview_enable_dcor},
- {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS,
+ preview_enable_dcor,
+ offsetof(struct prev_params, dcor),
+ FIELD_SIZEOF(struct prev_params, dcor),
+ offsetof(struct omap3isp_prev_update_config, dcor),
+ }, /* OMAP3ISP_PREV_GAMMABYPASS */ {
NULL,
- preview_enable_gammabypass},
- {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE,
+ preview_enable_gammabypass,
+ }, /* OMAP3ISP_PREV_DRK_FRM_CAPTURE */ {
NULL,
- preview_enable_drkframe_capture},
- {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT,
+ preview_enable_drkframe_capture,
+ }, /* OMAP3ISP_PREV_DRK_FRM_SUBTRACT */ {
NULL,
- preview_enable_drkframe},
- {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING,
+ preview_enable_drkframe,
+ }, /* OMAP3ISP_PREV_LENS_SHADING */ {
preview_config_drkf_shadcomp,
- preview_enable_drkframe},
- {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER,
+ preview_enable_drkframe,
+ }, /* OMAP3ISP_PREV_NF */ {
preview_config_noisefilter,
- preview_enable_noisefilter},
- {OMAP3ISP_PREV_GAMMA, PREV_GAMMA,
+ preview_enable_noisefilter,
+ offsetof(struct prev_params, nf),
+ FIELD_SIZEOF(struct prev_params, nf),
+ offsetof(struct omap3isp_prev_update_config, nf),
+ }, /* OMAP3ISP_PREV_GAMMA */ {
preview_config_gammacorrn,
- NULL},
- {-1, PREV_CONTRAST,
+ NULL,
+ offsetof(struct prev_params, gamma),
+ FIELD_SIZEOF(struct prev_params, gamma),
+ offsetof(struct omap3isp_prev_update_config, gamma),
+ }, /* OMAP3ISP_PREV_CONTRAST */ {
preview_config_contrast,
- NULL},
- {-1, PREV_BRIGHTNESS,
+ NULL,
+ offsetof(struct prev_params, contrast),
+ 0, true,
+ }, /* OMAP3ISP_PREV_BRIGHTNESS */ {
preview_config_brightness,
- NULL},
+ NULL,
+ offsetof(struct prev_params, brightness),
+ 0, true,
+ },
};
/*
- * __preview_get_ptrs - helper function which return pointers to members
- * of params and config structures.
- * @params - pointer to preview_params structure.
- * @param - return pointer to appropriate structure field.
- * @configs - pointer to update config structure.
- * @config - return pointer to appropriate structure field.
- * @bit - for which feature to return pointers.
- * Return size of corresponding prev_params member
- */
-static u32
-__preview_get_ptrs(struct prev_params *params, void **param,
- struct omap3isp_prev_update_config *configs,
- void __user **config, u32 bit)
-{
-#define CHKARG(cfgs, cfg, field) \
- if (cfgs && cfg) { \
- *(cfg) = (cfgs)->field; \
- }
-
- switch (bit) {
- case PREV_HORZ_MEDIAN_FILTER:
- *param = &params->hmed;
- CHKARG(configs, config, hmed)
- return sizeof(params->hmed);
- case PREV_NOISE_FILTER:
- *param = &params->nf;
- CHKARG(configs, config, nf)
- return sizeof(params->nf);
- break;
- case PREV_CFA:
- *param = &params->cfa;
- CHKARG(configs, config, cfa)
- return sizeof(params->cfa);
- case PREV_LUMA_ENHANCE:
- *param = &params->luma;
- CHKARG(configs, config, luma)
- return sizeof(params->luma);
- case PREV_CHROMA_SUPPRESS:
- *param = &params->csup;
- CHKARG(configs, config, csup)
- return sizeof(params->csup);
- case PREV_DEFECT_COR:
- *param = &params->dcor;
- CHKARG(configs, config, dcor)
- return sizeof(params->dcor);
- case PREV_BLKADJ:
- *param = &params->blk_adj;
- CHKARG(configs, config, blkadj)
- return sizeof(params->blk_adj);
- case PREV_YCLIMITS:
- *param = &params->yclimit;
- CHKARG(configs, config, yclimit)
- return sizeof(params->yclimit);
- case PREV_RGB2RGB:
- *param = &params->rgb2rgb;
- CHKARG(configs, config, rgb2rgb)
- return sizeof(params->rgb2rgb);
- case PREV_COLOR_CONV:
- *param = &params->rgb2ycbcr;
- CHKARG(configs, config, csc)
- return sizeof(params->rgb2ycbcr);
- case PREV_WB:
- *param = &params->wbal;
- CHKARG(configs, config, wbal)
- return sizeof(params->wbal);
- case PREV_GAMMA:
- *param = &params->gamma;
- CHKARG(configs, config, gamma)
- return sizeof(params->gamma);
- case PREV_CONTRAST:
- *param = &params->contrast;
- return 0;
- case PREV_BRIGHTNESS:
- *param = &params->brightness;
- return 0;
- default:
- *param = NULL;
- *config = NULL;
- break;
- }
- return 0;
-}
-
-/*
* preview_config - Copy and update local structure with userspace preview
* configuration.
* @prev: ISP preview engine
@@ -885,84 +909,103 @@ __preview_get_ptrs(struct prev_params *params, void **param,
static int preview_config(struct isp_prev_device *prev,
struct omap3isp_prev_update_config *cfg)
{
- struct prev_params *params;
- struct preview_update *attr;
- int i, bit, rval = 0;
+ unsigned long flags;
+ unsigned int i;
+ int rval = 0;
+ u32 update;
+ u32 active;
- params = &prev->params;
+ if (cfg->update == 0)
+ return 0;
- if (prev->state != ISP_PIPELINE_STREAM_STOPPED) {
- unsigned long flags;
+ /* Mark the shadow parameters we're going to update as busy. */
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_lock(prev, cfg->update, true);
+ active = prev->params.active;
+ spin_unlock_irqrestore(&prev->params.lock, flags);
- spin_lock_irqsave(&prev->lock, flags);
- prev->shadow_update = 1;
- spin_unlock_irqrestore(&prev->lock, flags);
- }
+ update = 0;
for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
- attr = &update_attrs[i];
- bit = 0;
+ const struct preview_update *attr = &update_attrs[i];
+ struct prev_params *params;
+ unsigned int bit = 1 << i;
- if (!(cfg->update & attr->cfg_bit))
+ if (attr->skip || !(cfg->update & bit))
continue;
- bit = cfg->flag & attr->cfg_bit;
- if (bit) {
- void *to = NULL, __user *from = NULL;
- unsigned long sz = 0;
+ params = &prev->params.params[!!(active & bit)];
+
+ if (cfg->flag & bit) {
+ void __user *from = *(void * __user *)
+ ((void *)cfg + attr->config_offset);
+ void *to = (void *)params + attr->param_offset;
+ size_t size = attr->param_size;
- sz = __preview_get_ptrs(params, &to, cfg, &from,
- bit);
- if (to && from && sz) {
- if (copy_from_user(to, from, sz)) {
+ if (to && from && size) {
+ if (copy_from_user(to, from, size)) {
rval = -EFAULT;
break;
}
}
- params->features |= attr->feature_bit;
+ params->features |= bit;
} else {
- params->features &= ~attr->feature_bit;
+ params->features &= ~bit;
}
- prev->update |= attr->feature_bit;
+ update |= bit;
}
- prev->shadow_update = 0;
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_unlock(prev, update, true);
+ preview_params_switch(prev);
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+
return rval;
}
/*
* preview_setup_hw - Setup preview registers and/or internal memory
* @prev: pointer to preview private structure
+ * @update: Bitmask of parameters to setup
+ * @active: Bitmask of parameters active in set 0
* Note: can be called from interrupt context
* Return none
*/
-static void preview_setup_hw(struct isp_prev_device *prev)
+static void preview_setup_hw(struct isp_prev_device *prev, u32 update,
+ u32 active)
{
- struct prev_params *params = &prev->params;
- struct preview_update *attr;
- int i, bit;
- void *param_ptr;
+ unsigned int i;
+ u32 features;
+
+ if (update == 0)
+ return;
+
+ features = (prev->params.params[0].features & active)
+ | (prev->params.params[1].features & ~active);
for (i = 0; i < ARRAY_SIZE(update_attrs); i++) {
- attr = &update_attrs[i];
+ const struct preview_update *attr = &update_attrs[i];
+ struct prev_params *params;
+ unsigned int bit = 1 << i;
+ void *param_ptr;
- if (!(prev->update & attr->feature_bit))
+ if (!(update & bit))
continue;
- bit = params->features & attr->feature_bit;
- if (bit) {
+
+ params = &prev->params.params[!(active & bit)];
+
+ if (params->features & bit) {
if (attr->config) {
- __preview_get_ptrs(params, &param_ptr, NULL,
- NULL, bit);
+ param_ptr = (void *)params + attr->param_offset;
attr->config(prev, param_ptr);
}
if (attr->enable)
attr->enable(prev, 1);
- } else
+ } else {
if (attr->enable)
attr->enable(prev, 0);
-
- prev->update &= ~attr->feature_bit;
+ }
}
}
@@ -1000,13 +1043,17 @@ preview_config_ycpos(struct isp_prev_device *prev,
static void preview_config_averager(struct isp_prev_device *prev, u8 average)
{
struct isp_device *isp = to_isp_device(prev);
+ struct prev_params *params;
int reg = 0;
- if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER)
+ params = (prev->params.active & OMAP3ISP_PREV_CFA)
+ ? &prev->params.params[0] : &prev->params.params[1];
+
+ if (params->cfa.format == OMAP3ISP_CFAFMT_BAYER)
reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT |
ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT |
average;
- else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON)
+ else if (params->cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON)
reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT |
ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT |
average;
@@ -1014,6 +1061,27 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
}
/*
+ * preview_config_input_format - Configure the input format
+ * @prev: The preview engine
+ * @format: Format on the preview engine sink pad
+ *
+ * Enable CFA interpolation for Bayer formats and disable it for greyscale
+ * formats.
+ */
+static void preview_config_input_format(struct isp_prev_device *prev,
+ const struct v4l2_mbus_framefmt *format)
+{
+ struct isp_device *isp = to_isp_device(prev);
+
+ if (format->code != V4L2_MBUS_FMT_Y10_1X10)
+ isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_CFAEN);
+ else
+ isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
+ ISPPRV_PCR_CFAEN);
+}
+
+/*
* preview_config_input_size - Configure the input frame size
*
* The preview engine crops several rows and columns internally depending on
@@ -1024,32 +1092,37 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average)
*
* See the explanation at the PREV_MARGIN_* definitions for more details.
*/
-static void preview_config_input_size(struct isp_prev_device *prev)
+static void preview_config_input_size(struct isp_prev_device *prev, u32 active)
{
+ const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK];
struct isp_device *isp = to_isp_device(prev);
- struct prev_params *params = &prev->params;
unsigned int sph = prev->crop.left;
unsigned int eph = prev->crop.left + prev->crop.width - 1;
unsigned int slv = prev->crop.top;
unsigned int elv = prev->crop.top + prev->crop.height - 1;
+ u32 features;
- if (params->features & PREV_CFA) {
+ if (format->code == V4L2_MBUS_FMT_Y10_1X10) {
sph -= 2;
eph += 2;
slv -= 2;
elv += 2;
}
- if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) {
+
+ features = (prev->params.params[0].features & active)
+ | (prev->params.params[1].features & ~active);
+
+ if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) {
sph -= 2;
eph += 2;
slv -= 2;
elv += 2;
}
- if (params->features & PREV_HORZ_MEDIAN_FILTER) {
+ if (features & OMAP3ISP_PREV_HRZ_MED) {
sph -= 2;
eph += 2;
}
- if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE))
+ if (features & (OMAP3ISP_PREV_CHROMA_SUPP | OMAP3ISP_PREV_LUMAENH))
sph -= 2;
isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph,
@@ -1184,8 +1257,16 @@ int omap3isp_preview_busy(struct isp_prev_device *prev)
*/
void omap3isp_preview_restore_context(struct isp_device *isp)
{
- isp->isp_prev.update = PREV_FEATURES_END - 1;
- preview_setup_hw(&isp->isp_prev);
+ struct isp_prev_device *prev = &isp->isp_prev;
+ const u32 update = OMAP3ISP_PREV_FEATURES_END - 1;
+
+ prev->params.params[0].update = prev->params.active & update;
+ prev->params.params[1].update = ~prev->params.active & update;
+
+ preview_setup_hw(prev, update, prev->params.active);
+
+ prev->params.params[0].update = 0;
+ prev->params.params[1].update = 0;
}
/*
@@ -1244,12 +1325,21 @@ static void preview_print_status(struct isp_prev_device *prev)
/*
* preview_init_params - init image processing parameters.
* @prev: pointer to previewer private structure
- * return none
*/
static void preview_init_params(struct isp_prev_device *prev)
{
- struct prev_params *params = &prev->params;
- int i = 0;
+ struct prev_params *params;
+ unsigned int i;
+
+ spin_lock_init(&prev->params.lock);
+
+ prev->params.active = ~0;
+ prev->params.params[0].busy = 0;
+ prev->params.params[0].update = OMAP3ISP_PREV_FEATURES_END - 1;
+ prev->params.params[1].busy = 0;
+ prev->params.params[1].update = 0;
+
+ params = &prev->params.params[0];
/* Init values */
params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS;
@@ -1277,22 +1367,22 @@ static void preview_init_params(struct isp_prev_device *prev)
params->wbal.coef1 = FLR_WBAL_COEF;
params->wbal.coef2 = FLR_WBAL_COEF;
params->wbal.coef3 = FLR_WBAL_COEF;
- params->blk_adj.red = FLR_BLKADJ_RED;
- params->blk_adj.green = FLR_BLKADJ_GREEN;
- params->blk_adj.blue = FLR_BLKADJ_BLUE;
+ params->blkadj.red = FLR_BLKADJ_RED;
+ params->blkadj.green = FLR_BLKADJ_GREEN;
+ params->blkadj.blue = FLR_BLKADJ_BLUE;
params->rgb2rgb = flr_rgb2rgb;
- params->rgb2ycbcr = flr_prev_csc;
+ params->csc = flr_prev_csc;
params->yclimit.minC = ISPPRV_YC_MIN;
params->yclimit.maxC = ISPPRV_YC_MAX;
params->yclimit.minY = ISPPRV_YC_MIN;
params->yclimit.maxY = ISPPRV_YC_MAX;
- params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER
- | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS
- | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB
- | PREV_BRIGHTNESS | PREV_CONTRAST;
-
- prev->update = PREV_FEATURES_END - 1;
+ params->features = OMAP3ISP_PREV_CFA | OMAP3ISP_PREV_DEFECT_COR
+ | OMAP3ISP_PREV_NF | OMAP3ISP_PREV_GAMMA
+ | OMAP3ISP_PREV_BLKADJ | OMAP3ISP_PREV_YC_LIMIT
+ | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV
+ | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS
+ | OMAP3ISP_PREV_CONTRAST;
}
/*
@@ -1321,8 +1411,17 @@ static void preview_configure(struct isp_prev_device *prev)
{
struct isp_device *isp = to_isp_device(prev);
struct v4l2_mbus_framefmt *format;
+ unsigned long flags;
+ u32 update;
+ u32 active;
- preview_setup_hw(prev);
+ spin_lock_irqsave(&prev->params.lock, flags);
+ /* Mark all active parameters we are going to touch as busy. */
+ update = preview_params_lock(prev, 0, false);
+ active = prev->params.active;
+ spin_unlock_irqrestore(&prev->params.lock, flags);
+
+ preview_setup_hw(prev, update, active);
if (prev->output & PREVIEW_OUTPUT_MEMORY)
isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR,
@@ -1343,7 +1442,8 @@ static void preview_configure(struct isp_prev_device *prev)
preview_adjust_bandwidth(prev);
- preview_config_input_size(prev);
+ preview_config_input_format(prev, format);
+ preview_config_input_size(prev, active);
if (prev->input == PREVIEW_INPUT_CCDC)
preview_config_inlineoffset(prev, 0);
@@ -1360,6 +1460,10 @@ static void preview_configure(struct isp_prev_device *prev)
preview_config_averager(prev, 0);
preview_config_ycpos(prev, format->code);
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_unlock(prev, update, false);
+ spin_unlock_irqrestore(&prev->params.lock, flags);
}
/* -----------------------------------------------------------------------------
@@ -1448,25 +1552,30 @@ static void preview_isr_buffer(struct isp_prev_device *prev)
void omap3isp_preview_isr(struct isp_prev_device *prev)
{
unsigned long flags;
+ u32 update;
+ u32 active;
if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping))
return;
- spin_lock_irqsave(&prev->lock, flags);
- if (prev->shadow_update)
- goto done;
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_switch(prev);
+ update = preview_params_lock(prev, 0, false);
+ active = prev->params.active;
+ spin_unlock_irqrestore(&prev->params.lock, flags);
- preview_setup_hw(prev);
- preview_config_input_size(prev);
-
-done:
- spin_unlock_irqrestore(&prev->lock, flags);
+ preview_setup_hw(prev, update, active);
+ preview_config_input_size(prev, active);
if (prev->input == PREVIEW_INPUT_MEMORY ||
prev->output & PREVIEW_OUTPUT_MEMORY)
preview_isr_buffer(prev);
else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS)
preview_enable_oneshot(prev);
+
+ spin_lock_irqsave(&prev->params.lock, flags);
+ preview_params_unlock(prev, update, false);
+ spin_unlock_irqrestore(&prev->params.lock, flags);
}
/* -----------------------------------------------------------------------------
@@ -1552,7 +1661,6 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable)
struct isp_video *video_out = &prev->video_out;
struct isp_device *isp = to_isp_device(prev);
struct device *dev = to_device(prev);
- unsigned long flags;
if (prev->state == ISP_PIPELINE_STREAM_STOPPED) {
if (enable == ISP_PIPELINE_STREAM_STOPPED)
@@ -1589,11 +1697,9 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable)
if (omap3isp_module_sync_idle(&sd->entity, &prev->wait,
&prev->stopping))
dev_dbg(dev, "%s: stop timeout.\n", sd->name);
- spin_lock_irqsave(&prev->lock, flags);
omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ);
omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE);
omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW);
- spin_unlock_irqrestore(&prev->lock, flags);
isp_video_dmaqueue_flags_clr(video_out);
break;
}
@@ -1624,6 +1730,7 @@ __preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh,
/* previewer format descriptions */
static const unsigned int preview_input_fmts[] = {
+ V4L2_MBUS_FMT_Y10_1X10,
V4L2_MBUS_FMT_SGRBG10_1X10,
V4L2_MBUS_FMT_SRGGB10_1X10,
V4L2_MBUS_FMT_SBGGR10_1X10,
@@ -1822,55 +1929,89 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
}
/*
- * preview_get_crop - Retrieve the crop rectangle on a pad
+ * preview_get_selection - Retrieve a selection rectangle on a pad
* @sd: ISP preview V4L2 subdevice
* @fh: V4L2 subdev file handle
- * @crop: crop rectangle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangles are the crop rectangles on the sink pad.
*
* Return 0 on success or a negative error code otherwise.
*/
-static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
+static int preview_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
{
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ if (sel->pad != PREV_PAD_SINK)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = INT_MAX;
+ sel->r.height = INT_MAX;
+
+ format = __preview_get_format(prev, fh, PREV_PAD_SINK,
+ sel->which);
+ preview_try_crop(prev, format, &sel->r);
+ break;
+
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ sel->r = *__preview_get_crop(prev, fh, sel->which);
+ break;
- /* Cropping is only supported on the sink pad. */
- if (crop->pad != PREV_PAD_SINK)
+ default:
return -EINVAL;
+ }
- crop->rect = *__preview_get_crop(prev, fh, crop->which);
return 0;
}
/*
- * preview_set_crop - Retrieve the crop rectangle on a pad
+ * preview_set_selection - Set a selection rectangle on a pad
* @sd: ISP preview V4L2 subdevice
* @fh: V4L2 subdev file handle
- * @crop: crop rectangle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangle is the actual crop rectangle on the sink pad.
*
* Return 0 on success or a negative error code otherwise.
*/
-static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
+static int preview_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
{
struct isp_prev_device *prev = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *format;
- /* Cropping is only supported on the sink pad. */
- if (crop->pad != PREV_PAD_SINK)
+ if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+ sel->pad != PREV_PAD_SINK)
return -EINVAL;
/* The crop rectangle can't be changed while streaming. */
if (prev->state != ISP_PIPELINE_STREAM_STOPPED)
return -EBUSY;
- format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which);
- preview_try_crop(prev, format, &crop->rect);
- *__preview_get_crop(prev, fh, crop->which) = crop->rect;
+ /* Modifying the crop rectangle always changes the format on the source
+ * pad. If the KEEP_CONFIG flag is set, just return the current crop
+ * rectangle.
+ */
+ if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) {
+ sel->r = *__preview_get_crop(prev, fh, sel->which);
+ return 0;
+ }
+
+ format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which);
+ preview_try_crop(prev, format, &sel->r);
+ *__preview_get_crop(prev, fh, sel->which) = sel->r;
/* Update the source format. */
- format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which);
- preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which);
+ format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which);
+ preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which);
return 0;
}
@@ -1979,8 +2120,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = {
.enum_frame_size = preview_enum_frame_size,
.get_fmt = preview_get_format,
.set_fmt = preview_set_format,
- .get_crop = preview_get_crop,
- .set_crop = preview_set_crop,
+ .get_selection = preview_get_selection,
+ .set_selection = preview_set_selection,
};
/* subdev operations */
@@ -2076,6 +2217,7 @@ static int preview_link_setup(struct media_entity *entity,
/* media operations */
static const struct media_entity_operations preview_media_ops = {
.link_setup = preview_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
};
void omap3isp_preview_unregister_entities(struct isp_prev_device *prev)
@@ -2201,7 +2343,7 @@ error_video_in:
}
/*
- * isp_preview_init - Previewer initialization.
+ * omap3isp_preview_init - Previewer initialization.
* @dev : Pointer to ISP device
* return -ENOMEM or zero on success
*/
@@ -2209,8 +2351,8 @@ int omap3isp_preview_init(struct isp_device *isp)
{
struct isp_prev_device *prev = &isp->isp_prev;
- spin_lock_init(&prev->lock);
init_waitqueue_head(&prev->wait);
+
preview_init_params(prev);
return preview_init_entities(prev);
diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h
index 09686607973..6663ab64e4b 100644
--- a/drivers/media/video/omap3isp/isppreview.h
+++ b/drivers/media/video/omap3isp/isppreview.h
@@ -45,29 +45,10 @@
#define ISPPRV_CONTRAST_HIGH 0xFF
#define ISPPRV_CONTRAST_UNITS 0x1
-/* Features list */
-#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH
-#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW
-#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED
-#define PREV_CFA OMAP3ISP_PREV_CFA
-#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP
-#define PREV_WB OMAP3ISP_PREV_WB
-#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ
-#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB
-#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV
-#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT
-#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR
-#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS
-#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE
-#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT
-#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING
-#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF
-#define PREV_GAMMA OMAP3ISP_PREV_GAMMA
-
-#define PREV_CONTRAST (1 << 17)
-#define PREV_BRIGHTNESS (1 << 18)
-#define PREV_AVERAGER (1 << 19)
-#define PREV_FEATURES_END (1 << 20)
+/* Additional features not listed in linux/omap3isp.h */
+#define OMAP3ISP_PREV_CONTRAST (1 << 17)
+#define OMAP3ISP_PREV_BRIGHTNESS (1 << 18)
+#define OMAP3ISP_PREV_FEATURES_END (1 << 19)
enum preview_input_entity {
PREVIEW_INPUT_NONE,
@@ -88,6 +69,8 @@ enum preview_ycpos_mode {
/*
* struct prev_params - Structure for all configuration
+ * @busy: Bitmask of busy parameters (being updated or used)
+ * @update: Bitmask of the parameters to be updated
* @features: Set of features enabled.
* @cfa: CFA coefficients.
* @csup: Chroma suppression coefficients.
@@ -96,15 +79,17 @@ enum preview_ycpos_mode {
* @dcor: Noise filter coefficients.
* @gamma: Gamma coefficients.
* @wbal: White Balance parameters.
- * @blk_adj: Black adjustment parameters.
+ * @blkadj: Black adjustment parameters.
* @rgb2rgb: RGB blending parameters.
- * @rgb2ycbcr: RGB to ycbcr parameters.
+ * @csc: Color space conversion (RGB to YCbCr) parameters.
* @hmed: Horizontal median filter.
* @yclimit: YC limits parameters.
* @contrast: Contrast.
* @brightness: Brightness.
*/
struct prev_params {
+ u32 busy;
+ u32 update;
u32 features;
struct omap3isp_prev_cfa cfa;
struct omap3isp_prev_csup csup;
@@ -113,35 +98,15 @@ struct prev_params {
struct omap3isp_prev_dcor dcor;
struct omap3isp_prev_gtables gamma;
struct omap3isp_prev_wbal wbal;
- struct omap3isp_prev_blkadj blk_adj;
+ struct omap3isp_prev_blkadj blkadj;
struct omap3isp_prev_rgbtorgb rgb2rgb;
- struct omap3isp_prev_csc rgb2ycbcr;
+ struct omap3isp_prev_csc csc;
struct omap3isp_prev_hmed hmed;
struct omap3isp_prev_yclimit yclimit;
u8 contrast;
u8 brightness;
};
-/*
- * struct isptables_update - Structure for Table Configuration.
- * @update: Specifies which tables should be updated.
- * @flag: Specifies which tables should be enabled.
- * @nf: Pointer to structure for Noise Filter
- * @lsc: Pointer to LSC gain table. (currently not used)
- * @gamma: Pointer to gamma correction tables.
- * @cfa: Pointer to color filter array configuration.
- * @wbal: Pointer to colour and digital gain configuration.
- */
-struct isptables_update {
- u32 update;
- u32 flag;
- struct omap3isp_prev_nf *nf;
- u32 *lsc;
- struct omap3isp_prev_gtables *gamma;
- struct omap3isp_prev_cfa *cfa;
- struct omap3isp_prev_wbal *wbal;
-};
-
/* Sink and source previewer pads */
#define PREV_PAD_SINK 0
#define PREV_PAD_SOURCE 1
@@ -157,12 +122,11 @@ struct isptables_update {
* @output: Bitmask of the active output
* @video_in: Input video entity
* @video_out: Output video entity
- * @params: Module configuration data
- * @shadow_update: If set, update the hardware configured in the next interrupt
+ * @params.params : Active and shadow parameters sets
+ * @params.active: Bitmask of parameters active in set 0
+ * @params.lock: Parameters lock, protects params.active and params.shadow
* @underrun: Whether the preview entity has queued buffers on the output
* @state: Current preview pipeline state
- * @lock: Shadow update lock
- * @update: Bitmask of the parameters to be updated
*
* This structure is used to store the OMAP ISP Preview module Information.
*/
@@ -179,13 +143,15 @@ struct isp_prev_device {
struct isp_video video_in;
struct isp_video video_out;
- struct prev_params params;
- unsigned int shadow_update:1;
+ struct {
+ struct prev_params params[2];
+ u32 active;
+ spinlock_t lock;
+ } params;
+
enum isp_pipeline_stream_state state;
wait_queue_head_t wait;
atomic_t stopping;
- spinlock_t lock;
- u32 update;
};
struct isp_device;
diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h
index 92c5a12157d..908dfd712e8 100644
--- a/drivers/media/video/omap3isp/ispqueue.h
+++ b/drivers/media/video/omap3isp/ispqueue.h
@@ -90,7 +90,7 @@ struct isp_video_buffer {
void *vaddr;
/* For userspace buffers. */
- unsigned long vm_flags;
+ vm_flags_t vm_flags;
unsigned long offset;
unsigned int npages;
struct page **pages;
diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c
index 6958a9e3dc2..14041c9c864 100644
--- a/drivers/media/video/omap3isp/ispresizer.c
+++ b/drivers/media/video/omap3isp/ispresizer.c
@@ -1188,32 +1188,6 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable)
}
/*
- * resizer_g_crop - handle get crop subdev operation
- * @sd : pointer to v4l2 subdev structure
- * @pad : subdev pad
- * @crop : pointer to crop structure
- * @which : active or try format
- * return zero
- */
-static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
-{
- struct isp_res_device *res = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
- struct resizer_ratio ratio;
-
- /* Only sink pad has crop capability */
- if (crop->pad != RESZ_PAD_SINK)
- return -EINVAL;
-
- format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which);
- crop->rect = *__resizer_get_crop(res, fh, crop->which);
- resizer_calc_ratios(res, &crop->rect, format, &ratio);
-
- return 0;
-}
-
-/*
* resizer_try_crop - mangles crop parameters.
*/
static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
@@ -1223,7 +1197,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
const unsigned int spv = DEFAULT_PHASE;
const unsigned int sph = DEFAULT_PHASE;
- /* Crop rectangle is constrained to the output size so that zoom ratio
+ /* Crop rectangle is constrained by the output size so that zoom ratio
* cannot exceed +/-4.0.
*/
unsigned int min_width =
@@ -1248,51 +1222,115 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
}
/*
- * resizer_s_crop - handle set crop subdev operation
- * @sd : pointer to v4l2 subdev structure
- * @pad : subdev pad
- * @crop : pointer to crop structure
- * @which : active or try format
- * return -EINVAL or zero when succeed
+ * resizer_get_selection - Retrieve a selection rectangle on a pad
+ * @sd: ISP resizer V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangles are the crop rectangles on the sink pad.
+ *
+ * Return 0 on success or a negative error code otherwise.
*/
-static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
+static int resizer_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct isp_res_device *res = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format_source;
+ struct v4l2_mbus_framefmt *format_sink;
+ struct resizer_ratio ratio;
+
+ if (sel->pad != RESZ_PAD_SINK)
+ return -EINVAL;
+
+ format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
+ sel->which);
+ format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
+ sel->which);
+
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = INT_MAX;
+ sel->r.height = INT_MAX;
+
+ resizer_try_crop(format_sink, format_source, &sel->r);
+ resizer_calc_ratios(res, &sel->r, format_source, &ratio);
+ break;
+
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ sel->r = *__resizer_get_crop(res, fh, sel->which);
+ resizer_calc_ratios(res, &sel->r, format_source, &ratio);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * resizer_set_selection - Set a selection rectangle on a pad
+ * @sd: ISP resizer V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ * @sel: Selection rectangle
+ *
+ * The only supported rectangle is the actual crop rectangle on the sink pad.
+ *
+ * FIXME: This function currently behaves as if the KEEP_CONFIG selection flag
+ * was always set.
+ *
+ * Return 0 on success or a negative error code otherwise.
+ */
+static int resizer_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
{
struct isp_res_device *res = v4l2_get_subdevdata(sd);
struct isp_device *isp = to_isp_device(res);
struct v4l2_mbus_framefmt *format_sink, *format_source;
struct resizer_ratio ratio;
- /* Only sink pad has crop capability */
- if (crop->pad != RESZ_PAD_SINK)
+ if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+ sel->pad != RESZ_PAD_SINK)
return -EINVAL;
format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK,
- crop->which);
+ sel->which);
format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE,
- crop->which);
+ sel->which);
dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__,
- crop->rect.left, crop->rect.top, crop->rect.width,
- crop->rect.height, crop->which);
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+ sel->which);
dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__,
format_sink->width, format_sink->height,
format_source->width, format_source->height);
- resizer_try_crop(format_sink, format_source, &crop->rect);
- *__resizer_get_crop(res, fh, crop->which) = crop->rect;
- resizer_calc_ratios(res, &crop->rect, format_source, &ratio);
+ /* Clamp the crop rectangle to the bounds, and then mangle it further to
+ * fulfill the TRM equations. Store the clamped but otherwise unmangled
+ * rectangle to avoid cropping the input multiple times: when an
+ * application sets the output format, the current crop rectangle is
+ * mangled during crop rectangle computation, which would lead to a new,
+ * smaller input crop rectangle every time the output size is set if we
+ * stored the mangled rectangle.
+ */
+ resizer_try_crop(format_sink, format_source, &sel->r);
+ *__resizer_get_crop(res, fh, sel->which) = sel->r;
+ resizer_calc_ratios(res, &sel->r, format_source, &ratio);
- if (crop->which == V4L2_SUBDEV_FORMAT_TRY)
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY)
return 0;
res->ratio = ratio;
- res->crop.active = crop->rect;
+ res->crop.active = sel->r;
/*
- * s_crop can be called while streaming is on. In this case
- * the crop values will be set in the next IRQ.
+ * set_selection can be called while streaming is on. In this case the
+ * crop values will be set in the next IRQ.
*/
if (res->state != ISP_PIPELINE_STREAM_STOPPED)
res->applycrop = 1;
@@ -1530,8 +1568,8 @@ static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = {
.enum_frame_size = resizer_enum_frame_size,
.get_fmt = resizer_get_format,
.set_fmt = resizer_set_format,
- .get_crop = resizer_g_crop,
- .set_crop = resizer_s_crop,
+ .get_selection = resizer_get_selection,
+ .set_selection = resizer_set_selection,
};
/* subdev operations */
@@ -1603,6 +1641,7 @@ static int resizer_link_setup(struct media_entity *entity,
/* media operations */
static const struct media_entity_operations resizer_media_ops = {
.link_setup = resizer_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
};
void omap3isp_resizer_unregister_entities(struct isp_res_device *res)
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c
index 11871ecc6d2..b8640be692f 100644
--- a/drivers/media/video/omap3isp/ispstat.c
+++ b/drivers/media/video/omap3isp/ispstat.c
@@ -1032,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
if (sub->type != stat->event_type)
return -EINVAL;
- return v4l2_event_subscribe(fh, sub, STAT_NEVENTS);
+ return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL);
}
int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c
index b0207005772..b37379d39cd 100644
--- a/drivers/media/video/omap3isp/ispvideo.c
+++ b/drivers/media/video/omap3isp/ispvideo.c
@@ -46,6 +46,10 @@
* Helper functions
*/
+/*
+ * NOTE: When adding new media bus codes, always remember to add
+ * corresponding in-memory formats to the table below!!!
+ */
static struct isp_format_info formats[] = {
{ V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8,
@@ -68,9 +72,18 @@ static struct isp_format_info formats[] = {
{ V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8,
V4L2_PIX_FMT_SRGGB8, 8, },
+ { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SBGGR10_1X10, 0,
+ V4L2_PIX_FMT_SBGGR10DPCM8, 8, },
+ { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SGBRG10_1X10, 0,
+ V4L2_PIX_FMT_SGBRG10DPCM8, 8, },
{ V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
V4L2_MBUS_FMT_SGRBG10_1X10, 0,
V4L2_PIX_FMT_SGRBG10DPCM8, 8, },
+ { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8,
+ V4L2_MBUS_FMT_SRGGB10_1X10, 0,
+ V4L2_PIX_FMT_SRGGB10DPCM8, 8, },
{ V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10,
V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8,
V4L2_PIX_FMT_SBGGR10, 10, },
@@ -117,37 +130,6 @@ omap3isp_video_format_info(enum v4l2_mbus_pixelcode code)
}
/*
- * Decide whether desired output pixel code can be obtained with
- * the lane shifter by shifting the input pixel code.
- * @in: input pixelcode to shifter
- * @out: output pixelcode from shifter
- * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0]
- *
- * return true if the combination is possible
- * return false otherwise
- */
-static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in,
- enum v4l2_mbus_pixelcode out,
- unsigned int additional_shift)
-{
- const struct isp_format_info *in_info, *out_info;
-
- if (in == out)
- return true;
-
- in_info = omap3isp_video_format_info(in);
- out_info = omap3isp_video_format_info(out);
-
- if ((in_info->flavor == 0) || (out_info->flavor == 0))
- return false;
-
- if (in_info->flavor != out_info->flavor)
- return false;
-
- return in_info->bpp - out_info->bpp + additional_shift <= 6;
-}
-
-/*
* isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format
* @video: ISP video instance
* @mbus: v4l2_mbus_framefmt format (input)
@@ -242,8 +224,8 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad)
}
/* Return a pointer to the ISP video instance at the far end of the pipeline. */
-static struct isp_video *
-isp_video_far_end(struct isp_video *video)
+static int isp_video_get_graph_data(struct isp_video *video,
+ struct isp_pipeline *pipe)
{
struct media_entity_graph graph;
struct media_entity *entity = &video->video.entity;
@@ -254,21 +236,38 @@ isp_video_far_end(struct isp_video *video)
media_entity_graph_walk_start(&graph, entity);
while ((entity = media_entity_graph_walk_next(&graph))) {
+ struct isp_video *__video;
+
+ pipe->entities |= 1 << entity->id;
+
+ if (far_end != NULL)
+ continue;
+
if (entity == &video->video.entity)
continue;
if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE)
continue;
- far_end = to_isp_video(media_entity_to_video_device(entity));
- if (far_end->type != video->type)
- break;
-
- far_end = NULL;
+ __video = to_isp_video(media_entity_to_video_device(entity));
+ if (__video->type != video->type)
+ far_end = __video;
}
mutex_unlock(&mdev->graph_mutex);
- return far_end;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ pipe->input = far_end;
+ pipe->output = video;
+ } else {
+ if (far_end == NULL)
+ return -EPIPE;
+
+ pipe->input = video;
+ pipe->output = far_end;
+ }
+
+ return 0;
}
/*
@@ -285,52 +284,24 @@ isp_video_far_end(struct isp_video *video)
static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
{
struct isp_device *isp = pipe->output->isp;
- struct v4l2_subdev_format fmt_source;
- struct v4l2_subdev_format fmt_sink;
struct media_pad *pad;
struct v4l2_subdev *subdev;
- int ret;
-
- pipe->max_rate = pipe->l3_ick;
subdev = isp_video_remote_subdev(pipe->output, NULL);
if (subdev == NULL)
return -EPIPE;
while (1) {
- unsigned int shifter_link;
/* Retrieve the sink format */
pad = &subdev->entity.pads[0];
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
- fmt_sink.pad = pad->index;
- fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return -EPIPE;
-
/* Update the maximum frame rate */
if (subdev == &isp->isp_res.subdev)
omap3isp_resizer_max_rate(&isp->isp_res,
&pipe->max_rate);
- /* Check ccdc maximum data rate when data comes from sensor
- * TODO: Include ccdc rate in pipe->max_rate and compare the
- * total pipe rate with the input data rate from sensor.
- */
- if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) {
- unsigned int rate = UINT_MAX;
-
- omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate);
- if (isp->isp_ccdc.vpcfg.pixelclk > rate)
- return -ENOSPC;
- }
-
- /* If sink pad is on CCDC, the link has the lane shifter
- * in the middle of it. */
- shifter_link = subdev == &isp->isp_ccdc.subdev;
-
/* Retrieve the source format. Return an error if no source
* entity can be found, and stop checking the pipeline if the
* source entity isn't a subdev.
@@ -343,32 +314,6 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe)
break;
subdev = media_entity_to_v4l2_subdev(pad->entity);
-
- fmt_source.pad = pad->index;
- fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE;
- ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return -EPIPE;
-
- /* Check if the two ends match */
- if (fmt_source.format.width != fmt_sink.format.width ||
- fmt_source.format.height != fmt_sink.format.height)
- return -EPIPE;
-
- if (shifter_link) {
- unsigned int parallel_shift = 0;
- if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) {
- struct isp_parallel_platform_data *pdata =
- &((struct isp_v4l2_subdevs_group *)
- subdev->host_priv)->bus.parallel;
- parallel_shift = pdata->data_lane_shift * 2;
- }
- if (!isp_video_is_shiftable(fmt_source.format.code,
- fmt_sink.format.code,
- parallel_shift))
- return -EPIPE;
- } else if (fmt_source.format.code != fmt_sink.format.code)
- return -EPIPE;
}
return 0;
@@ -923,6 +868,92 @@ isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b)
file->f_flags & O_NONBLOCK);
}
+static int isp_video_check_external_subdevs(struct isp_video *video,
+ struct isp_pipeline *pipe)
+{
+ struct isp_device *isp = video->isp;
+ struct media_entity *ents[] = {
+ &isp->isp_csi2a.subdev.entity,
+ &isp->isp_csi2c.subdev.entity,
+ &isp->isp_ccp2.subdev.entity,
+ &isp->isp_ccdc.subdev.entity
+ };
+ struct media_pad *source_pad;
+ struct media_entity *source = NULL;
+ struct media_entity *sink;
+ struct v4l2_subdev_format fmt;
+ struct v4l2_ext_controls ctrls;
+ struct v4l2_ext_control ctrl;
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ents); i++) {
+ /* Is the entity part of the pipeline? */
+ if (!(pipe->entities & (1 << ents[i]->id)))
+ continue;
+
+ /* ISP entities have always sink pad == 0. Find source. */
+ source_pad = media_entity_remote_source(&ents[i]->pads[0]);
+ if (source_pad == NULL)
+ continue;
+
+ source = source_pad->entity;
+ sink = ents[i];
+ break;
+ }
+
+ if (!source) {
+ dev_warn(isp->dev, "can't find source, failing now\n");
+ return ret;
+ }
+
+ if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return 0;
+
+ pipe->external = media_entity_to_v4l2_subdev(source);
+
+ fmt.pad = source_pad->index;
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink),
+ pad, get_fmt, NULL, &fmt);
+ if (unlikely(ret < 0)) {
+ dev_warn(isp->dev, "get_fmt returned null!\n");
+ return ret;
+ }
+
+ pipe->external_bpp = omap3isp_video_format_info(fmt.format.code)->bpp;
+
+ memset(&ctrls, 0, sizeof(ctrls));
+ memset(&ctrl, 0, sizeof(ctrl));
+
+ ctrl.id = V4L2_CID_PIXEL_RATE;
+
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+
+ ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls);
+ if (ret < 0) {
+ dev_warn(isp->dev, "no pixel rate control in subdev %s\n",
+ pipe->external->name);
+ return ret;
+ }
+
+ pipe->external_rate = ctrl.value64;
+
+ if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) {
+ unsigned int rate = UINT_MAX;
+ /*
+ * Check that maximum allowed CCDC pixel rate isn't
+ * exceeded by the pixel rate.
+ */
+ omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate);
+ if (pipe->external_rate > rate)
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
/*
* Stream management
*
@@ -961,7 +992,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
struct isp_video *video = video_drvdata(file);
enum isp_pipeline_state state;
struct isp_pipeline *pipe;
- struct isp_video *far_end;
unsigned long flags;
int ret;
@@ -980,46 +1010,45 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
*/
pipe = video->video.entity.pipe
? to_isp_pipeline(&video->video.entity) : &video->pipe;
- media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+
+ pipe->entities = 0;
+
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, true);
+ pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
+ pipe->max_rate = pipe->l3_ick;
+
+ ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+ if (ret < 0)
+ goto err_pipeline_start;
/* Verify that the currently configured format matches the output of
* the connected subdev.
*/
ret = isp_video_check_format(video, vfh);
if (ret < 0)
- goto error;
+ goto err_check_format;
video->bpl_padding = ret;
video->bpl_value = vfh->format.fmt.pix.bytesperline;
- /* Find the ISP video node connected at the far end of the pipeline and
- * update the pipeline.
- */
- far_end = isp_video_far_end(video);
+ ret = isp_video_get_graph_data(video, pipe);
+ if (ret < 0)
+ goto err_check_format;
- if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT;
- pipe->input = far_end;
- pipe->output = video;
- } else {
- if (far_end == NULL) {
- ret = -EPIPE;
- goto error;
- }
-
+ else
state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT;
- pipe->input = video;
- pipe->output = far_end;
- }
- if (video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, true);
- pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
+ ret = isp_video_check_external_subdevs(video, pipe);
+ if (ret < 0)
+ goto err_check_format;
/* Validate the pipeline and update its state. */
ret = isp_video_validate_pipeline(pipe);
if (ret < 0)
- goto error;
+ goto err_check_format;
pipe->error = false;
@@ -1041,7 +1070,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
ret = omap3isp_video_queue_streamon(&vfh->queue);
if (ret < 0)
- goto error;
+ goto err_check_format;
/* In sensor-to-memory mode, the stream can be started synchronously
* to the stream on command. In memory-to-memory mode, it will be
@@ -1051,32 +1080,34 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
ret = omap3isp_pipeline_set_stream(pipe,
ISP_PIPELINE_STREAM_CONTINUOUS);
if (ret < 0)
- goto error;
+ goto err_set_stream;
spin_lock_irqsave(&video->queue->irqlock, flags);
if (list_empty(&video->dmaqueue))
video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN;
spin_unlock_irqrestore(&video->queue->irqlock, flags);
}
-error:
- if (ret < 0) {
- omap3isp_video_queue_streamoff(&vfh->queue);
- if (video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, false);
- media_entity_pipeline_stop(&video->video.entity);
- /* The DMA queue must be emptied here, otherwise CCDC interrupts
- * that will get triggered the next time the CCDC is powered up
- * will try to access buffers that might have been freed but
- * still present in the DMA queue. This can easily get triggered
- * if the above omap3isp_pipeline_set_stream() call fails on a
- * system with a free-running sensor.
- */
- INIT_LIST_HEAD(&video->dmaqueue);
- video->queue = NULL;
- }
+ video->streaming = 1;
+
+ mutex_unlock(&video->stream_lock);
+ return 0;
- if (!ret)
- video->streaming = 1;
+err_set_stream:
+ omap3isp_video_queue_streamoff(&vfh->queue);
+err_check_format:
+ media_entity_pipeline_stop(&video->video.entity);
+err_pipeline_start:
+ if (video->isp->pdata->set_constraints)
+ video->isp->pdata->set_constraints(video->isp, false);
+ /* The DMA queue must be emptied here, otherwise CCDC interrupts that
+ * will get triggered the next time the CCDC is powered up will try to
+ * access buffers that might have been freed but still present in the
+ * DMA queue. This can easily get triggered if the above
+ * omap3isp_pipeline_set_stream() call fails on a system with a
+ * free-running sensor.
+ */
+ INIT_LIST_HEAD(&video->dmaqueue);
+ video->queue = NULL;
mutex_unlock(&video->stream_lock);
return ret;
diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h
index d91bdb919be..5acc909500e 100644
--- a/drivers/media/video/omap3isp/ispvideo.h
+++ b/drivers/media/video/omap3isp/ispvideo.h
@@ -88,6 +88,7 @@ enum isp_pipeline_state {
/*
* struct isp_pipeline - An ISP hardware pipeline
* @error: A hardware error occurred during capture
+ * @entities: Bitmask of entities in the pipeline (indexed by entity ID)
*/
struct isp_pipeline {
struct media_pipeline pipe;
@@ -96,12 +97,16 @@ struct isp_pipeline {
enum isp_pipeline_stream_state stream_state;
struct isp_video *input;
struct isp_video *output;
+ u32 entities;
unsigned long l3_ick;
unsigned int max_rate;
atomic_t frame_number;
bool do_propagation; /* of frame number */
bool error;
struct v4l2_fract max_timeperframe;
+ struct v4l2_subdev *external;
+ unsigned int external_rate;
+ unsigned int external_bpp;
};
#define to_isp_pipeline(__e) \
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
index 80e07794ac8..0bc93313d37 100644
--- a/drivers/media/video/ov5642.c
+++ b/drivers/media/video/ov5642.c
@@ -1025,8 +1025,6 @@ static int ov5642_probe(struct i2c_client *client,
priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2;
priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2;
- priv->crop_rect.width = OV5642_DEFAULT_WIDTH;
- priv->crop_rect.height = OV5642_DEFAULT_HEIGHT;
priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH;
priv->total_height = BLANKING_MIN_HEIGHT;
diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c
index e753b5e4d2c..af2d9086d7e 100644
--- a/drivers/media/video/pms.c
+++ b/drivers/media/video/pms.c
@@ -30,15 +30,19 @@
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
+#include <linux/isa.h>
#include <asm/io.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-device.h>
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.4");
+MODULE_VERSION("0.0.5");
#define MOTOROLA 1
#define PHILIPS2 2 /* SAA7191 */
@@ -55,11 +59,11 @@ struct i2c_info {
struct pms {
struct v4l2_device v4l2_dev;
struct video_device vdev;
+ struct v4l2_ctrl_handler hdl;
int height;
int width;
int depth;
int input;
- s32 brightness, saturation, hue, contrast;
struct mutex lock;
int i2c_count;
struct i2c_info i2cinfo[64];
@@ -72,8 +76,6 @@ struct pms {
void __iomem *mem;
};
-static struct pms pms_card;
-
/*
* I/O ports and Shared Memory
*/
@@ -676,8 +678,10 @@ static int pms_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card));
- strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info));
- vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ snprintf(vcap->bus_info, sizeof(vcap->bus_info),
+ "ISA:%s", dev->v4l2_dev.name);
+ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -716,11 +720,9 @@ static int pms_s_input(struct file *file, void *fh, unsigned int inp)
if (inp > 3)
return -EINVAL;
- mutex_lock(&dev->lock);
dev->input = inp;
pms_videosource(dev, inp & 1);
pms_vcrinput(dev, inp >> 1);
- mutex_unlock(&dev->lock);
return 0;
}
@@ -738,7 +740,6 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
int ret = 0;
dev->std = *std;
- mutex_lock(&dev->lock);
if (dev->std & V4L2_STD_NTSC) {
pms_framerate(dev, 30);
pms_secamcross(dev, 0);
@@ -762,81 +763,31 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std)
pms_format(dev, 0);
break;
}*/
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int pms_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
- }
- return -EINVAL;
-}
-
-static int pms_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct pms *dev = video_drvdata(file);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = dev->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = dev->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = dev->saturation;
- break;
- case V4L2_CID_HUE:
- ctrl->value = dev->hue;
- break;
- default:
- ret = -EINVAL;
- break;
- }
return ret;
}
-static int pms_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int pms_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct pms *dev = video_drvdata(file);
+ struct pms *dev = container_of(ctrl->handler, struct pms, hdl);
int ret = 0;
- mutex_lock(&dev->lock);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- dev->brightness = ctrl->value;
- pms_brightness(dev, dev->brightness);
+ pms_brightness(dev, ctrl->val);
break;
case V4L2_CID_CONTRAST:
- dev->contrast = ctrl->value;
- pms_contrast(dev, dev->contrast);
+ pms_contrast(dev, ctrl->val);
break;
case V4L2_CID_SATURATION:
- dev->saturation = ctrl->value;
- pms_saturation(dev, dev->saturation);
+ pms_saturation(dev, ctrl->val);
break;
case V4L2_CID_HUE:
- dev->hue = ctrl->value;
- pms_hue(dev, dev->hue);
+ pms_hue(dev, ctrl->val);
break;
default:
ret = -EINVAL;
break;
}
- mutex_unlock(&dev->lock);
return ret;
}
@@ -884,13 +835,11 @@ static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fm
if (ret)
return ret;
- mutex_lock(&dev->lock);
dev->width = pix->width;
dev->height = pix->height;
dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16;
pms_resolution(dev, dev->width, dev->height);
/* Ok we figured out what to use from our wide choice */
- mutex_unlock(&dev->lock);
return 0;
}
@@ -901,7 +850,7 @@ static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc
"RGB 5:5:5", V4L2_PIX_FMT_RGB555,
{ 0, 0, 0, 0 }
},
- { 0, 0, 0,
+ { 1, 0, 0,
"RGB 5:6:5", V4L2_PIX_FMT_RGB565,
{ 0, 0, 0, 0 }
},
@@ -922,32 +871,43 @@ static ssize_t pms_read(struct file *file, char __user *buf,
struct pms *dev = video_drvdata(file);
int len;
- mutex_lock(&dev->lock);
len = pms_capture(dev, buf, (dev->depth == 15), count);
- mutex_unlock(&dev->lock);
return len;
}
+static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct v4l2_fh *fh = file->private_data;
+ unsigned int res = POLLIN | POLLRDNORM;
+
+ if (v4l2_event_pending(fh))
+ res |= POLLPRI;
+ poll_wait(file, &fh->wait, wait);
+ return res;
+}
+
static const struct v4l2_file_operations pms_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = pms_poll,
.unlocked_ioctl = video_ioctl2,
.read = pms_read,
};
static const struct v4l2_ioctl_ops pms_ioctl_ops = {
- .vidioc_querycap = pms_querycap,
- .vidioc_g_input = pms_g_input,
- .vidioc_s_input = pms_s_input,
- .vidioc_enum_input = pms_enum_input,
- .vidioc_g_std = pms_g_std,
- .vidioc_s_std = pms_s_std,
- .vidioc_queryctrl = pms_queryctrl,
- .vidioc_g_ctrl = pms_g_ctrl,
- .vidioc_s_ctrl = pms_s_ctrl,
- .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
+ .vidioc_querycap = pms_querycap,
+ .vidioc_g_input = pms_g_input,
+ .vidioc_s_input = pms_s_input,
+ .vidioc_enum_input = pms_enum_input,
+ .vidioc_g_std = pms_g_std,
+ .vidioc_s_std = pms_s_std,
+ .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
/*
@@ -956,7 +916,6 @@ static const struct v4l2_ioctl_ops pms_ioctl_ops = {
static int init_mediavision(struct pms *dev)
{
- int id;
int idec, decst;
int i;
static const unsigned char i2c_defs[] = {
@@ -988,7 +947,6 @@ static int init_mediavision(struct pms *dev)
outb(dev->io >> 4, 0x9a01); /* Set IO port */
- id = mvv_read(dev, 3);
decst = pms_i2c_stat(dev, 0x43);
if (decst != -1)
@@ -1068,76 +1026,125 @@ static int enable;
module_param(enable, int, 0);
#endif
-static int __init pms_init(void)
+static const struct v4l2_ctrl_ops pms_ctrl_ops = {
+ .s_ctrl = pms_s_ctrl,
+};
+
+static int pms_probe(struct device *pdev, unsigned int card)
{
- struct pms *dev = &pms_card;
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ struct pms *dev;
+ struct v4l2_device *v4l2_dev;
+ struct v4l2_ctrl_handler *hdl;
int res;
- strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name));
-
- v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n");
-
#ifndef MODULE
if (!enable) {
- v4l2_err(v4l2_dev,
- "PMS: not enabled, use pms.enable=1 to probe\n");
+ pr_err("PMS: not enabled, use pms.enable=1 to probe\n");
return -ENODEV;
}
#endif
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOMEM;
+
dev->decoder = PHILIPS2;
dev->io = io_port;
dev->data = io_port + 1;
+ v4l2_dev = &dev->v4l2_dev;
+ hdl = &dev->hdl;
- if (init_mediavision(dev)) {
+ res = v4l2_device_register(pdev, v4l2_dev);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ goto free_dev;
+ }
+ v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n");
+
+ res = init_mediavision(dev);
+ if (res) {
v4l2_err(v4l2_dev, "Board not found.\n");
- return -ENODEV;
+ goto free_io;
}
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 139);
+ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 255, 1, 70);
+ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 255, 1, 64);
+ v4l2_ctrl_new_std(hdl, &pms_ctrl_ops,
+ V4L2_CID_HUE, 0, 255, 1, 0);
+ if (hdl->error) {
+ res = hdl->error;
+ goto free_hdl;
}
+ mutex_init(&dev->lock);
strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
dev->vdev.v4l2_dev = v4l2_dev;
+ dev->vdev.ctrl_handler = hdl;
dev->vdev.fops = &pms_fops;
dev->vdev.ioctl_ops = &pms_ioctl_ops;
dev->vdev.release = video_device_release_empty;
+ dev->vdev.lock = &dev->lock;
+ dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+ set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
video_set_drvdata(&dev->vdev, dev);
- mutex_init(&dev->lock);
dev->std = V4L2_STD_NTSC_M;
dev->height = 240;
dev->width = 320;
- dev->depth = 15;
- dev->brightness = 139;
- dev->contrast = 70;
- dev->hue = 0;
- dev->saturation = 64;
+ dev->depth = 16;
pms_swsense(dev, 75);
pms_resolution(dev, 320, 240);
pms_videosource(dev, 0);
pms_vcrinput(dev, 0);
- if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- release_region(dev->io, 3);
- release_region(0x9a01, 1);
- iounmap(dev->mem);
- return -EINVAL;
- }
- return 0;
+ v4l2_ctrl_handler_setup(hdl);
+ res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr);
+ if (res >= 0)
+ return 0;
+
+free_hdl:
+ v4l2_ctrl_handler_free(hdl);
+ v4l2_device_unregister(&dev->v4l2_dev);
+free_io:
+ release_region(dev->io, 3);
+ release_region(0x9a01, 1);
+ iounmap(dev->mem);
+free_dev:
+ kfree(dev);
+ return res;
}
-static void __exit pms_exit(void)
+static int pms_remove(struct device *pdev, unsigned int card)
{
- struct pms *dev = &pms_card;
+ struct pms *dev = dev_get_drvdata(pdev);
video_unregister_device(&dev->vdev);
+ v4l2_ctrl_handler_free(&dev->hdl);
release_region(dev->io, 3);
release_region(0x9a01, 1);
iounmap(dev->mem);
+ return 0;
+}
+
+static struct isa_driver pms_driver = {
+ .probe = pms_probe,
+ .remove = pms_remove,
+ .driver = {
+ .name = "pms",
+ },
+};
+
+static int __init pms_init(void)
+{
+ return isa_register_driver(&pms_driver, 1);
+}
+
+static void __exit pms_exit(void)
+{
+ isa_unregister_driver(&pms_driver);
}
module_init(pms_init);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index 305e6aaa844..036952f2a3c 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -317,18 +317,16 @@ struct pvr2_hdw {
v4l2_std_id std_mask_eeprom; // Hardware supported selections
v4l2_std_id std_mask_avail; // Which standards we may select from
v4l2_std_id std_mask_cur; // Currently selected standard(s)
- unsigned int std_enum_cnt; // # of enumerated standards
int std_enum_cur; // selected standard enumeration value
int std_dirty; // True if std_mask_cur has changed
struct pvr2_ctl_info std_info_enum;
struct pvr2_ctl_info std_info_avail;
struct pvr2_ctl_info std_info_cur;
- struct v4l2_standard *std_defs;
- const char **std_enum_names;
+ struct pvr2_ctl_info std_info_detect;
// Generated string names, one per actual V4L2 standard
const char *std_mask_ptrs[32];
- char std_mask_names[32][10];
+ char std_mask_names[32][16];
int unit_number; /* ID for driver instance */
unsigned long serial_number; /* ID for hardware itself */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index ebc2c7e3923..fb828ba1dbb 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -334,8 +334,6 @@ static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
-static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
-static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
static void pvr2_hdw_quiescent_timeout(unsigned long);
static void pvr2_hdw_decoder_stabilization_timeout(unsigned long);
static void pvr2_hdw_encoder_wait_timeout(unsigned long);
@@ -346,7 +344,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
void *write_data,unsigned int write_len,
void *read_data,unsigned int read_len);
static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);
-
+static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw);
static void trace_stbit(const char *name,int val)
{
@@ -840,6 +838,12 @@ static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp)
return 0;
}
+static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp)
+{
+ *vp = pvr2_hdw_get_detected_std(cptr->hdw);
+ return 0;
+}
+
static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp)
{
*vp = cptr->hdw->std_mask_avail;
@@ -854,8 +858,7 @@ static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v)
ns = (ns & ~m) | (v & m);
if (ns == hdw->std_mask_avail) return 0;
hdw->std_mask_avail = ns;
- pvr2_hdw_internal_set_std_avail(hdw);
- pvr2_hdw_internal_find_stdenum(hdw);
+ hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
return 0;
}
@@ -895,7 +898,6 @@ static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v)
if (ns == hdw->std_mask_cur) return 0;
hdw->std_mask_cur = ns;
hdw->std_dirty = !0;
- pvr2_hdw_internal_find_stdenum(hdw);
return 0;
}
@@ -941,40 +943,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
}
-static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v)
-{
- struct pvr2_hdw *hdw = cptr->hdw;
- if (v < 0) return -EINVAL;
- if (v > hdw->std_enum_cnt) return -EINVAL;
- hdw->std_enum_cur = v;
- if (!v) return 0;
- v--;
- if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0;
- hdw->std_mask_cur = hdw->std_defs[v].id;
- hdw->std_dirty = !0;
- return 0;
-}
-
-
-static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp)
-{
- *vp = cptr->hdw->std_enum_cur;
- return 0;
-}
-
-
-static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr)
-{
- return cptr->hdw->std_dirty != 0;
-}
-
-
-static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr)
-{
- cptr->hdw->std_dirty = 0;
-}
-
-
#define DEFINT(vmin,vmax) \
.type = pvr2_ctl_int, \
.def.type_int.min_value = vmin, \
@@ -1293,15 +1261,14 @@ static const struct pvr2_ctl_info control_defs[] = {
.sym_to_val = ctrl_std_sym_to_val,
.type = pvr2_ctl_bitmask,
},{
- .desc = "Video Standard Name",
- .name = "video_standard",
- .internal_id = PVR2_CID_STDENUM,
+ .desc = "Video Standards Detected Mask",
+ .name = "video_standard_mask_detected",
+ .internal_id = PVR2_CID_STDDETECT,
.skip_init = !0,
- .get_value = ctrl_stdenumcur_get,
- .set_value = ctrl_stdenumcur_set,
- .is_dirty = ctrl_stdenumcur_is_dirty,
- .clear_dirty = ctrl_stdenumcur_clear_dirty,
- .type = pvr2_ctl_enum,
+ .get_value = ctrl_stddetect_get,
+ .val_to_sym = ctrl_std_val_to_sym,
+ .sym_to_val = ctrl_std_sym_to_val,
+ .type = pvr2_ctl_bitmask,
}
};
@@ -1936,7 +1903,7 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
hdw->std_mask_avail |= std2;
}
- pvr2_hdw_internal_set_std_avail(hdw);
+ hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
if (std1) {
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1);
@@ -1945,7 +1912,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
bcnt,buf);
hdw->std_mask_cur = std1;
hdw->std_dirty = !0;
- pvr2_hdw_internal_find_stdenum(hdw);
return;
}
if (std3) {
@@ -1955,7 +1921,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
" (determined by device type): %.*s",bcnt,buf);
hdw->std_mask_cur = std3;
hdw->std_dirty = !0;
- pvr2_hdw_internal_find_stdenum(hdw);
return;
}
@@ -1975,24 +1940,10 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
bcnt,buf);
hdw->std_mask_cur = std_eeprom_maps[idx].std;
hdw->std_dirty = !0;
- pvr2_hdw_internal_find_stdenum(hdw);
return;
}
}
- if (hdw->std_enum_cnt > 1) {
- // Autoselect the first listed standard
- hdw->std_enum_cur = 1;
- hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id;
- hdw->std_dirty = !0;
- pvr2_trace(PVR2_TRACE_STD,
- "Initial video standard auto-selected to %s",
- hdw->std_defs[hdw->std_enum_cur-1].name);
- return;
- }
-
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Unable to select a viable initial video standard");
}
@@ -2594,14 +2545,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
cptr->info = ciptr;
}
- // Initialize video standard enum dynamic control
- cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM);
- if (cptr) {
- memcpy(&hdw->std_info_enum,cptr->info,
- sizeof(hdw->std_info_enum));
- cptr->info = &hdw->std_info_enum;
-
- }
// Initialize control data regarding video standard masks
valid_std_mask = pvr2_std_get_usable();
for (idx = 0; idx < 32; idx++) {
@@ -2629,7 +2572,17 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
cptr->info = &hdw->std_info_cur;
hdw->std_info_cur.def.type_bitmask.bit_names =
hdw->std_mask_ptrs;
- hdw->std_info_avail.def.type_bitmask.valid_bits =
+ hdw->std_info_cur.def.type_bitmask.valid_bits =
+ valid_std_mask;
+ }
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT);
+ if (cptr) {
+ memcpy(&hdw->std_info_detect,cptr->info,
+ sizeof(hdw->std_info_detect));
+ cptr->info = &hdw->std_info_detect;
+ hdw->std_info_detect.def.type_bitmask.bit_names =
+ hdw->std_mask_ptrs;
+ hdw->std_info_detect.def.type_bitmask.valid_bits =
valid_std_mask;
}
@@ -2711,8 +2664,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
kfree(hdw->ctl_write_buffer);
kfree(hdw->controls);
kfree(hdw->mpeg_ctrl_info);
- kfree(hdw->std_defs);
- kfree(hdw->std_enum_names);
kfree(hdw);
}
return NULL;
@@ -2788,8 +2739,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
} while (0); mutex_unlock(&pvr2_unit_mtx);
kfree(hdw->controls);
kfree(hdw->mpeg_ctrl_info);
- kfree(hdw->std_defs);
- kfree(hdw->std_enum_names);
kfree(hdw);
}
@@ -2812,86 +2761,6 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
}
-// Attempt to autoselect an appropriate value for std_enum_cur given
-// whatever is currently in std_mask_cur
-static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw)
-{
- unsigned int idx;
- for (idx = 1; idx < hdw->std_enum_cnt; idx++) {
- if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) {
- hdw->std_enum_cur = idx;
- return;
- }
- }
- hdw->std_enum_cur = 0;
-}
-
-
-// Calculate correct set of enumerated standards based on currently known
-// set of available standards bits.
-static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw)
-{
- struct v4l2_standard *newstd;
- unsigned int std_cnt;
- unsigned int idx;
-
- newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail);
-
- if (hdw->std_defs) {
- kfree(hdw->std_defs);
- hdw->std_defs = NULL;
- }
- hdw->std_enum_cnt = 0;
- if (hdw->std_enum_names) {
- kfree(hdw->std_enum_names);
- hdw->std_enum_names = NULL;
- }
-
- if (!std_cnt) {
- pvr2_trace(
- PVR2_TRACE_ERROR_LEGS,
- "WARNING: Failed to identify any viable standards");
- }
-
- /* Set up the dynamic control for this standard */
- hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL);
- if (hdw->std_enum_names) {
- hdw->std_enum_names[0] = "none";
- for (idx = 0; idx < std_cnt; idx++)
- hdw->std_enum_names[idx+1] = newstd[idx].name;
- hdw->std_info_enum.def.type_enum.value_names =
- hdw->std_enum_names;
- hdw->std_info_enum.def.type_enum.count = std_cnt+1;
- } else {
- pvr2_trace(
- PVR2_TRACE_ERROR_LEGS,
- "WARNING: Failed to alloc memory for names");
- hdw->std_info_enum.def.type_enum.value_names = NULL;
- hdw->std_info_enum.def.type_enum.count = 0;
- }
- hdw->std_defs = newstd;
- hdw->std_enum_cnt = std_cnt+1;
- hdw->std_enum_cur = 0;
- hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
-}
-
-
-int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,
- struct v4l2_standard *std,
- unsigned int idx)
-{
- int ret = -EINVAL;
- if (!idx) return ret;
- LOCK_TAKE(hdw->big_lock); do {
- if (idx >= hdw->std_enum_cnt) break;
- idx--;
- memcpy(std,hdw->std_defs+idx,sizeof(*std));
- ret = 0;
- } while (0); LOCK_GIVE(hdw->big_lock);
- return ret;
-}
-
-
/* Get the number of defined controls */
unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
{
@@ -2995,11 +2864,13 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
}
-int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std)
+v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw)
{
+ v4l2_std_id std;
+ std = (v4l2_std_id)hdw->std_mask_avail;
v4l2_device_call_all(&hdw->v4l2_dev, 0,
- video, querystd, std);
- return 0;
+ video, querystd, &std);
+ return std;
}
/* Execute whatever commands are required to update the state of all the
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 66546580b17..8060fc666ee 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -28,7 +28,6 @@
/* Private internal control ids, look these up with
pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */
-#define PVR2_CID_STDENUM 1
#define PVR2_CID_STDCUR 2
#define PVR2_CID_STDAVAIL 3
#define PVR2_CID_INPUT 4
@@ -46,6 +45,7 @@
#define PVR2_CID_CROPCAPBT 16
#define PVR2_CID_CROPCAPBW 17
#define PVR2_CID_CROPCAPBH 18
+#define PVR2_CID_STDDETECT 19
/* Legal values for the INPUT state variable */
#define PVR2_CVAL_INPUT_TV 0
@@ -210,13 +210,6 @@ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
/* Get handle to video output stream */
struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
-/* Emit a video standard struct */
-int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
- unsigned int idx);
-
-/* Get the detected video standard */
-int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std);
-
/* Enable / disable retrieval of CPU firmware or prom contents. This must
be enabled before pvr2_hdw_cpufw_get() will function. Note that doing
this may prevent the device from running (and leaving this mode may
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index e1111d968a3..7bddfaeeafc 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -107,7 +107,6 @@ static struct v4l2_fmtdesc pvr_fmtdesc [] = {
// This should really be V4L2_PIX_FMT_MPEG, but xawtv
// breaks when I do that.
.pixelformat = 0, // V4L2_PIX_FMT_MPEG,
- .reserved = { 0, 0, 0, 0 }
}
};
@@ -145,740 +144,739 @@ static struct v4l2_format pvr_format [] = {
.start = { 0, 0 },
.count = { 0, 0 },
.flags = 0,
- .reserved = { 0, 0 }
}
}
}
};
+
/*
- * pvr_ioctl()
- *
- * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
- *
+ * This is part of Video 4 Linux API. These procedures handle ioctl() calls.
*/
-static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
{
struct pvr2_v4l2_fh *fh = file->private_data;
- struct pvr2_v4l2 *vp = fh->vhead;
- struct pvr2_v4l2_dev *pdi = fh->pdi;
struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- long ret = -EINVAL;
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
- v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd);
- }
+ memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+ strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw),
+ sizeof(cap->bus_info));
+ strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card));
+ return 0;
+}
- if (!pvr2_hdw_dev_ok(hdw)) {
- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "ioctl failed - bad or no context");
- return -EFAULT;
- }
+static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
- /* check priority */
- switch (cmd) {
- case VIDIOC_S_CTRL:
- case VIDIOC_S_STD:
- case VIDIOC_S_INPUT:
- case VIDIOC_S_TUNER:
- case VIDIOC_S_FREQUENCY:
- ret = v4l2_prio_check(&vp->prio, fh->prio);
- if (ret)
- return ret;
- }
+ *p = v4l2_prio_max(&vp->prio);
+ return 0;
+}
- switch (cmd) {
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
+static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
- memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
- strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw),
- sizeof(cap->bus_info));
- strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card));
+ return v4l2_prio_change(&vp->prio, &fh->prio, prio);
+}
- ret = 0;
- break;
- }
+static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
- case VIDIOC_G_PRIORITY:
- {
- enum v4l2_priority *p = arg;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val);
+ *std = val;
+ return ret;
+}
- *p = v4l2_prio_max(&vp->prio);
- ret = 0;
- break;
- }
+int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- case VIDIOC_S_PRIORITY:
- {
- enum v4l2_priority *prio = arg;
+ return pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std);
+}
- ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio);
- break;
- }
+static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *vs = (struct v4l2_standard *)arg;
- int idx = vs->index;
- ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1);
- break;
- }
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val);
+ *std = val;
+ return ret;
+}
- case VIDIOC_QUERYSTD:
- {
- v4l2_std_id *std = arg;
- *std = V4L2_STD_ALL;
- ret = pvr2_hdw_get_detected_std(hdw, std);
- break;
- }
+static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_ctrl *cptr;
+ struct v4l2_input tmp;
+ unsigned int cnt;
+ int val;
+ int ret;
- case VIDIOC_G_STD:
- {
- int val = 0;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val);
- *(v4l2_std_id *)arg = val;
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.index = vi->index;
+ ret = 0;
+ if (vi->index >= fh->input_cnt)
+ return -EINVAL;
+ val = fh->input_map[vi->index];
+ switch (val) {
+ case PVR2_CVAL_INPUT_TV:
+ case PVR2_CVAL_INPUT_DTV:
+ case PVR2_CVAL_INPUT_RADIO:
+ tmp.type = V4L2_INPUT_TYPE_TUNER;
break;
- }
-
- case VIDIOC_S_STD:
- {
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),
- *(v4l2_std_id *)arg);
+ case PVR2_CVAL_INPUT_SVIDEO:
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ tmp.type = V4L2_INPUT_TYPE_CAMERA;
break;
+ default:
+ return -EINVAL;
}
- case VIDIOC_ENUMINPUT:
- {
- struct pvr2_ctrl *cptr;
- struct v4l2_input *vi = (struct v4l2_input *)arg;
- struct v4l2_input tmp;
- unsigned int cnt;
- int val;
+ cnt = 0;
+ pvr2_ctrl_get_valname(cptr, val,
+ tmp.name, sizeof(tmp.name) - 1, &cnt);
+ tmp.name[cnt] = 0;
- cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
+ /* Don't bother with audioset, since this driver currently
+ always switches the audio whenever the video is
+ switched. */
- memset(&tmp,0,sizeof(tmp));
- tmp.index = vi->index;
- ret = 0;
- if (vi->index >= fh->input_cnt) {
- ret = -EINVAL;
- break;
- }
- val = fh->input_map[vi->index];
- switch (val) {
- case PVR2_CVAL_INPUT_TV:
- case PVR2_CVAL_INPUT_DTV:
- case PVR2_CVAL_INPUT_RADIO:
- tmp.type = V4L2_INPUT_TYPE_TUNER;
- break;
- case PVR2_CVAL_INPUT_SVIDEO:
- case PVR2_CVAL_INPUT_COMPOSITE:
- tmp.type = V4L2_INPUT_TYPE_CAMERA;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- if (ret < 0) break;
-
- cnt = 0;
- pvr2_ctrl_get_valname(cptr,val,
- tmp.name,sizeof(tmp.name)-1,&cnt);
- tmp.name[cnt] = 0;
-
- /* Don't bother with audioset, since this driver currently
- always switches the audio whenever the video is
- switched. */
-
- /* Handling std is a tougher problem. It doesn't make
- sense in cases where a device might be multi-standard.
- We could just copy out the current value for the
- standard, but it can change over time. For now just
- leave it zero. */
-
- memcpy(vi, &tmp, sizeof(tmp));
-
- ret = 0;
- break;
- }
+ /* Handling std is a tougher problem. It doesn't make
+ sense in cases where a device might be multi-standard.
+ We could just copy out the current value for the
+ standard, but it can change over time. For now just
+ leave it zero. */
+ *vi = tmp;
+ return 0;
+}
- case VIDIOC_G_INPUT:
- {
- unsigned int idx;
- struct pvr2_ctrl *cptr;
- struct v4l2_input *vi = (struct v4l2_input *)arg;
- int val;
- cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
- val = 0;
- ret = pvr2_ctrl_get_value(cptr,&val);
- vi->index = 0;
- for (idx = 0; idx < fh->input_cnt; idx++) {
- if (fh->input_map[idx] == val) {
- vi->index = idx;
- break;
- }
- }
- break;
- }
+static int pvr2_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int val;
+ int ret;
- case VIDIOC_S_INPUT:
- {
- struct v4l2_input *vi = (struct v4l2_input *)arg;
- if (vi->index >= fh->input_cnt) {
- ret = -ERANGE;
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
+ val = 0;
+ ret = pvr2_ctrl_get_value(cptr, &val);
+ *i = 0;
+ for (idx = 0; idx < fh->input_cnt; idx++) {
+ if (fh->input_map[idx] == val) {
+ *i = idx;
break;
}
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
- fh->input_map[vi->index]);
- break;
}
+ return ret;
+}
- case VIDIOC_ENUMAUDIO:
- {
- /* pkt: FIXME: We are returning one "fake" input here
- which could very well be called "whatever_we_like".
- This is for apps that want to see an audio input
- just to feel comfortable, as well as to test if
- it can do stereo or sth. There is actually no guarantee
- that the actual audio input cannot change behind the app's
- back, but most applications should not mind that either.
-
- Hopefully, mplayer people will work with us on this (this
- whole mess is to support mplayer pvr://), or Hans will come
- up with a more standard way to say "we have inputs but we
- don 't want you to change them independent of video" which
- will sort this mess.
- */
- struct v4l2_audio *vin = arg;
- ret = -EINVAL;
- if (vin->index > 0) break;
- strncpy(vin->name, "PVRUSB2 Audio",14);
- vin->capability = V4L2_AUDCAP_STEREO;
- ret = 0;
- break;
- break;
- }
+static int pvr2_s_input(struct file *file, void *priv, unsigned int inp)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- case VIDIOC_G_AUDIO:
- {
- /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
- struct v4l2_audio *vin = arg;
- memset(vin,0,sizeof(*vin));
- vin->index = 0;
- strncpy(vin->name, "PVRUSB2 Audio",14);
- vin->capability = V4L2_AUDCAP_STEREO;
- ret = 0;
- break;
- }
+ if (inp >= fh->input_cnt)
+ return -EINVAL;
+ return pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
+ fh->input_map[inp]);
+}
- case VIDIOC_G_TUNER:
- {
- struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin)
+{
+ /* pkt: FIXME: We are returning one "fake" input here
+ which could very well be called "whatever_we_like".
+ This is for apps that want to see an audio input
+ just to feel comfortable, as well as to test if
+ it can do stereo or sth. There is actually no guarantee
+ that the actual audio input cannot change behind the app's
+ back, but most applications should not mind that either.
+
+ Hopefully, mplayer people will work with us on this (this
+ whole mess is to support mplayer pvr://), or Hans will come
+ up with a more standard way to say "we have inputs but we
+ don 't want you to change them independent of video" which
+ will sort this mess.
+ */
+
+ if (vin->index > 0)
+ return -EINVAL;
+ strncpy(vin->name, "PVRUSB2 Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
- if (vt->index != 0) break; /* Only answer for the 1st tuner */
+static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin)
+{
+ /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */
+ vin->index = 0;
+ strncpy(vin->name, "PVRUSB2 Audio", 14);
+ vin->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+}
- pvr2_hdw_execute_tuner_poll(hdw);
- ret = pvr2_hdw_get_tuner_status(hdw,vt);
- break;
- }
+static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout)
+{
+ if (vout->index)
+ return -EINVAL;
+ return 0;
+}
- case VIDIOC_S_TUNER:
- {
- struct v4l2_tuner *vt=(struct v4l2_tuner *)arg;
+static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
- if (vt->index != 0)
- break;
+ if (vt->index != 0)
+ return -EINVAL; /* Only answer for the 1st tuner */
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE),
+ pvr2_hdw_execute_tuner_poll(hdw);
+ return pvr2_hdw_get_tuner_status(hdw, vt);
+}
+
+static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ if (vt->index != 0)
+ return -EINVAL;
+
+ return pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE),
vt->audmode);
- break;
- }
+}
- case VIDIOC_S_FREQUENCY:
- {
- const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
- unsigned long fv;
- struct v4l2_tuner vt;
- int cur_input;
- struct pvr2_ctrl *ctrlp;
- ret = pvr2_hdw_get_tuner_status(hdw,&vt);
- if (ret != 0) break;
- ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
- ret = pvr2_ctrl_get_value(ctrlp,&cur_input);
- if (ret != 0) break;
- if (vf->type == V4L2_TUNER_RADIO) {
- if (cur_input != PVR2_CVAL_INPUT_RADIO) {
- pvr2_ctrl_set_value(ctrlp,
- PVR2_CVAL_INPUT_RADIO);
- }
- } else {
- if (cur_input == PVR2_CVAL_INPUT_RADIO) {
- pvr2_ctrl_set_value(ctrlp,
- PVR2_CVAL_INPUT_TV);
- }
- }
- fv = vf->frequency;
- if (vt.capability & V4L2_TUNER_CAP_LOW) {
- fv = (fv * 125) / 2;
- } else {
- fv = fv * 62500;
- }
- ret = pvr2_ctrl_set_value(
+int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ unsigned long fv;
+ struct v4l2_tuner vt;
+ int cur_input;
+ struct pvr2_ctrl *ctrlp;
+ int ret;
+
+ ret = pvr2_hdw_get_tuner_status(hdw, &vt);
+ if (ret != 0)
+ return ret;
+ ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT);
+ ret = pvr2_ctrl_get_value(ctrlp, &cur_input);
+ if (ret != 0)
+ return ret;
+ if (vf->type == V4L2_TUNER_RADIO) {
+ if (cur_input != PVR2_CVAL_INPUT_RADIO)
+ pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO);
+ } else {
+ if (cur_input == PVR2_CVAL_INPUT_RADIO)
+ pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV);
+ }
+ fv = vf->frequency;
+ if (vt.capability & V4L2_TUNER_CAP_LOW)
+ fv = (fv * 125) / 2;
+ else
+ fv = fv * 62500;
+ return pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv);
- break;
- }
+}
- case VIDIOC_G_FREQUENCY:
- {
- struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
- int val = 0;
- int cur_input;
- struct v4l2_tuner vt;
- ret = pvr2_hdw_get_tuner_status(hdw,&vt);
- if (ret != 0) break;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),
+static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int cur_input;
+ struct v4l2_tuner vt;
+ int ret;
+
+ ret = pvr2_hdw_get_tuner_status(hdw, &vt);
+ if (ret != 0)
+ return ret;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY),
&val);
- if (ret != 0) break;
- pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
+ if (ret != 0)
+ return ret;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT),
&cur_input);
- if (cur_input == PVR2_CVAL_INPUT_RADIO) {
- vf->type = V4L2_TUNER_RADIO;
- } else {
- vf->type = V4L2_TUNER_ANALOG_TV;
- }
- if (vt.capability & V4L2_TUNER_CAP_LOW) {
- val = (val * 2) / 125;
- } else {
- val /= 62500;
- }
- vf->frequency = val;
- break;
- }
+ if (cur_input == PVR2_CVAL_INPUT_RADIO)
+ vf->type = V4L2_TUNER_RADIO;
+ else
+ vf->type = V4L2_TUNER_ANALOG_TV;
+ if (vt.capability & V4L2_TUNER_CAP_LOW)
+ val = (val * 2) / 125;
+ else
+ val /= 62500;
+ vf->frequency = val;
+ return 0;
+}
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg;
+static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd)
+{
+ /* Only one format is supported : mpeg.*/
+ if (fd->index != 0)
+ return -EINVAL;
- /* Only one format is supported : mpeg.*/
- if (fd->index != 0)
- break;
+ memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
+ return 0;
+}
- memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
- ret = 0;
- break;
- }
+static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val;
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *vf = (struct v4l2_format *)arg;
- int val;
- switch(vf->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
- sizeof(struct v4l2_format));
- val = 0;
- pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES),
- &val);
- vf->fmt.pix.width = val;
- val = 0;
- pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES),
- &val);
- vf->fmt.pix.height = val;
- ret = 0;
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- // ????? Still need to figure out to do VBI correctly
- ret = -EINVAL;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- break;
- }
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format));
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES),
+ &val);
+ vf->fmt.pix.width = val;
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES),
+ &val);
+ vf->fmt.pix.height = val;
+ return 0;
+}
- case VIDIOC_TRY_FMT:
- case VIDIOC_S_FMT:
- {
- struct v4l2_format *vf = (struct v4l2_format *)arg;
-
- ret = 0;
- switch(vf->type) {
- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
- int lmin,lmax,ldef;
- struct pvr2_ctrl *hcp,*vcp;
- int h = vf->fmt.pix.height;
- int w = vf->fmt.pix.width;
- hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES);
- vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES);
-
- lmin = pvr2_ctrl_get_min(hcp);
- lmax = pvr2_ctrl_get_max(hcp);
- pvr2_ctrl_get_def(hcp, &ldef);
- if (w == -1) {
- w = ldef;
- } else if (w < lmin) {
- w = lmin;
- } else if (w > lmax) {
- w = lmax;
- }
- lmin = pvr2_ctrl_get_min(vcp);
- lmax = pvr2_ctrl_get_max(vcp);
- pvr2_ctrl_get_def(vcp, &ldef);
- if (h == -1) {
- h = ldef;
- } else if (h < lmin) {
- h = lmin;
- } else if (h > lmax) {
- h = lmax;
- }
+static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int lmin, lmax, ldef;
+ struct pvr2_ctrl *hcp, *vcp;
+ int h = vf->fmt.pix.height;
+ int w = vf->fmt.pix.width;
+
+ hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
+ vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
+
+ lmin = pvr2_ctrl_get_min(hcp);
+ lmax = pvr2_ctrl_get_max(hcp);
+ pvr2_ctrl_get_def(hcp, &ldef);
+ if (w == -1)
+ w = ldef;
+ else if (w < lmin)
+ w = lmin;
+ else if (w > lmax)
+ w = lmax;
+ lmin = pvr2_ctrl_get_min(vcp);
+ lmax = pvr2_ctrl_get_max(vcp);
+ pvr2_ctrl_get_def(vcp, &ldef);
+ if (h == -1)
+ h = ldef;
+ else if (h < lmin)
+ h = lmin;
+ else if (h > lmax)
+ h = lmax;
+
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ vf->fmt.pix.width = w;
+ vf->fmt.pix.height = h;
+ return 0;
+}
- memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
- sizeof(struct v4l2_format));
- vf->fmt.pix.width = w;
- vf->fmt.pix.height = h;
+static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_ctrl *hcp, *vcp;
+ int ret = pvr2_try_fmt_vid_cap(file, fh, vf);
- if (cmd == VIDIOC_S_FMT) {
- pvr2_ctrl_set_value(hcp,vf->fmt.pix.width);
- pvr2_ctrl_set_value(vcp,vf->fmt.pix.height);
- }
- } break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- // ????? Still need to figure out to do VBI correctly
- ret = -EINVAL;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- break;
- }
+ if (ret)
+ return ret;
+ hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES);
+ vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES);
+ pvr2_ctrl_set_value(hcp, vf->fmt.pix.width);
+ pvr2_ctrl_set_value(vcp, vf->fmt.pix.height);
+ return 0;
+}
- case VIDIOC_STREAMON:
- {
- if (!fh->pdi->stream) {
- /* No stream defined for this node. This means
- that we're not currently allowed to stream from
- this node. */
- ret = -EPERM;
- break;
- }
- ret = pvr2_hdw_set_stream_type(hdw,pdi->config);
- if (ret < 0) return ret;
- ret = pvr2_hdw_set_streaming(hdw,!0);
- break;
+static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_v4l2_dev *pdi = fh->pdi;
+ int ret;
+
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means
+ that we're not currently allowed to stream from
+ this node. */
+ return -EPERM;
}
+ ret = pvr2_hdw_set_stream_type(hdw, pdi->config);
+ if (ret < 0)
+ return ret;
+ return pvr2_hdw_set_streaming(hdw, !0);
+}
- case VIDIOC_STREAMOFF:
- {
- if (!fh->pdi->stream) {
- /* No stream defined for this node. This means
- that we're not currently allowed to stream from
- this node. */
- ret = -EPERM;
- break;
- }
- ret = pvr2_hdw_set_streaming(hdw,0);
- break;
+static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ if (!fh->pdi->stream) {
+ /* No stream defined for this node. This means
+ that we're not currently allowed to stream from
+ this node. */
+ return -EPERM;
}
+ return pvr2_hdw_set_streaming(hdw, 0);
+}
- case VIDIOC_QUERYCTRL:
- {
- struct pvr2_ctrl *cptr;
- int val;
- struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;
- ret = 0;
- if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
- cptr = pvr2_hdw_get_ctrl_nextv4l(
- hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
- if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr);
- } else {
- cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id);
- }
- if (!cptr) {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "QUERYCTRL id=0x%x not implemented here",
- vc->id);
- ret = -EINVAL;
- break;
- }
+static int pvr2_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *vc)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct pvr2_ctrl *cptr;
+ int val;
+ int ret;
+ ret = 0;
+ if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+ cptr = pvr2_hdw_get_ctrl_nextv4l(
+ hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
+ if (cptr)
+ vc->id = pvr2_ctrl_get_v4lid(cptr);
+ } else {
+ cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id);
+ }
+ if (!cptr) {
pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "QUERYCTRL id=0x%x mapping name=%s (%s)",
- vc->id,pvr2_ctrl_get_name(cptr),
- pvr2_ctrl_get_desc(cptr));
- strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name));
- vc->flags = pvr2_ctrl_get_v4lflags(cptr);
- pvr2_ctrl_get_def(cptr, &val);
- vc->default_value = val;
- switch (pvr2_ctrl_get_type(cptr)) {
- case pvr2_ctl_enum:
- vc->type = V4L2_CTRL_TYPE_MENU;
- vc->minimum = 0;
- vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
- vc->step = 1;
- break;
- case pvr2_ctl_bool:
- vc->type = V4L2_CTRL_TYPE_BOOLEAN;
- vc->minimum = 0;
- vc->maximum = 1;
- vc->step = 1;
- break;
- case pvr2_ctl_int:
- vc->type = V4L2_CTRL_TYPE_INTEGER;
- vc->minimum = pvr2_ctrl_get_min(cptr);
- vc->maximum = pvr2_ctrl_get_max(cptr);
- vc->step = 1;
- break;
- default:
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "QUERYCTRL id=0x%x name=%s not mappable",
- vc->id,pvr2_ctrl_get_name(cptr));
- ret = -EINVAL;
- break;
- }
+ "QUERYCTRL id=0x%x not implemented here",
+ vc->id);
+ return -EINVAL;
+ }
+
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x mapping name=%s (%s)",
+ vc->id, pvr2_ctrl_get_name(cptr),
+ pvr2_ctrl_get_desc(cptr));
+ strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name));
+ vc->flags = pvr2_ctrl_get_v4lflags(cptr);
+ pvr2_ctrl_get_def(cptr, &val);
+ vc->default_value = val;
+ switch (pvr2_ctrl_get_type(cptr)) {
+ case pvr2_ctl_enum:
+ vc->type = V4L2_CTRL_TYPE_MENU;
+ vc->minimum = 0;
+ vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
+ vc->step = 1;
break;
- }
-
- case VIDIOC_QUERYMENU:
- {
- struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg;
- unsigned int cnt = 0;
- ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id),
- vm->index,
- vm->name,sizeof(vm->name)-1,
- &cnt);
- vm->name[cnt] = 0;
+ case pvr2_ctl_bool:
+ vc->type = V4L2_CTRL_TYPE_BOOLEAN;
+ vc->minimum = 0;
+ vc->maximum = 1;
+ vc->step = 1;
break;
- }
-
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *vc = (struct v4l2_control *)arg;
- int val = 0;
- ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
- &val);
- vc->value = val;
+ case pvr2_ctl_int:
+ vc->type = V4L2_CTRL_TYPE_INTEGER;
+ vc->minimum = pvr2_ctrl_get_min(cptr);
+ vc->maximum = pvr2_ctrl_get_max(cptr);
+ vc->step = 1;
break;
+ default:
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x name=%s not mappable",
+ vc->id, pvr2_ctrl_get_name(cptr));
+ return -EINVAL;
}
+ return 0;
+}
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *vc = (struct v4l2_control *)arg;
- ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
- vc->value);
- break;
- }
+static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ unsigned int cnt = 0;
+ int ret;
- case VIDIOC_G_EXT_CTRLS:
- {
- struct v4l2_ext_controls *ctls =
- (struct v4l2_ext_controls *)arg;
- struct v4l2_ext_control *ctrl;
- unsigned int idx;
- int val;
- ret = 0;
- for (idx = 0; idx < ctls->count; idx++) {
- ctrl = ctls->controls + idx;
- ret = pvr2_ctrl_get_value(
- pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val);
- if (ret) {
- ctls->error_idx = idx;
- break;
- }
- /* Ensure that if read as a 64 bit value, the user
- will still get a hopefully sane value */
- ctrl->value64 = 0;
- ctrl->value = val;
+ ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id),
+ vm->index,
+ vm->name, sizeof(vm->name) - 1,
+ &cnt);
+ vm->name[cnt] = 0;
+ return ret;
+}
+
+static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
+
+ ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
+ &val);
+ vc->value = val;
+ return ret;
+}
+
+static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id),
+ vc->value);
+}
+
+static int pvr2_g_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctls)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ int val;
+ int ret;
+
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val);
+ if (ret) {
+ ctls->error_idx = idx;
+ return ret;
}
- break;
+ /* Ensure that if read as a 64 bit value, the user
+ will still get a hopefully sane value */
+ ctrl->value64 = 0;
+ ctrl->value = val;
}
+ return 0;
+}
- case VIDIOC_S_EXT_CTRLS:
- {
- struct v4l2_ext_controls *ctls =
- (struct v4l2_ext_controls *)arg;
- struct v4l2_ext_control *ctrl;
- unsigned int idx;
- ret = 0;
- for (idx = 0; idx < ctls->count; idx++) {
- ctrl = ctls->controls + idx;
- ret = pvr2_ctrl_set_value(
- pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),
+static int pvr2_s_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctls)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ int ret;
+
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id),
ctrl->value);
- if (ret) {
- ctls->error_idx = idx;
- break;
- }
+ if (ret) {
+ ctls->error_idx = idx;
+ return ret;
}
- break;
}
+ return 0;
+}
- case VIDIOC_TRY_EXT_CTRLS:
- {
- struct v4l2_ext_controls *ctls =
- (struct v4l2_ext_controls *)arg;
- struct v4l2_ext_control *ctrl;
- struct pvr2_ctrl *pctl;
- unsigned int idx;
- /* For the moment just validate that the requested control
- actually exists. */
- ret = 0;
- for (idx = 0; idx < ctls->count; idx++) {
- ctrl = ctls->controls + idx;
- pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id);
- if (!pctl) {
- ret = -EINVAL;
- ctls->error_idx = idx;
- break;
- }
- }
- break;
- }
+static int pvr2_try_ext_ctrls(struct file *file, void *priv,
+ struct v4l2_ext_controls *ctls)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct v4l2_ext_control *ctrl;
+ struct pvr2_ctrl *pctl;
+ unsigned int idx;
+ int ret;
- case VIDIOC_CROPCAP:
- {
- struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg;
- if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- ret = -EINVAL;
- break;
+ /* For the moment just validate that the requested control
+ actually exists. */
+ ret = 0;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id);
+ if (!pctl) {
+ ctls->error_idx = idx;
+ return -EINVAL;
}
- ret = pvr2_hdw_get_cropcap(hdw, cap);
- cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
- break;
}
- case VIDIOC_G_CROP:
- {
- struct v4l2_crop *crop = (struct v4l2_crop *)arg;
- int val = 0;
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- ret = -EINVAL;
- break;
- }
- ret = pvr2_ctrl_get_value(
+ return 0;
+}
+
+static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret;
+
+ if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ ret = pvr2_hdw_get_cropcap(hdw, cap);
+ cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */
+ return ret;
+}
+
+static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int val = 0;
+ int ret;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- crop->c.left = val;
- ret = pvr2_ctrl_get_value(
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.left = val;
+ ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- crop->c.top = val;
- ret = pvr2_ctrl_get_value(
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.top = val;
+ ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- crop->c.width = val;
- ret = pvr2_ctrl_get_value(
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.width = val;
+ ret = pvr2_ctrl_get_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- crop->c.height = val;
- }
- case VIDIOC_S_CROP:
- {
- struct v4l2_crop *crop = (struct v4l2_crop *)arg;
- if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- ret = -EINVAL;
- break;
- }
- ret = pvr2_ctrl_set_value(
+ if (ret != 0)
+ return -EINVAL;
+ crop->c.height = val;
+ return 0;
+}
+
+static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ struct v4l2_cropcap cap;
+ int ret;
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL),
crop->c.left);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- ret = pvr2_ctrl_set_value(
+ if (ret != 0)
+ return -EINVAL;
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT),
crop->c.top);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- ret = pvr2_ctrl_set_value(
+ if (ret != 0)
+ return -EINVAL;
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW),
crop->c.width);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- ret = pvr2_ctrl_set_value(
+ if (ret != 0)
+ return -EINVAL;
+ ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH),
crop->c.height);
- if (ret != 0) {
- ret = -EINVAL;
- break;
- }
- }
- case VIDIOC_LOG_STATUS:
- {
- pvr2_hdw_trigger_module_log(hdw);
- ret = 0;
- break;
- }
+ if (ret != 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int pvr2_log_status(struct file *file, void *priv)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+
+ pvr2_hdw_trigger_module_log(hdw);
+ return 0;
+}
+
#ifdef CONFIG_VIDEO_ADV_DEBUG
- case VIDIOC_DBG_S_REGISTER:
- case VIDIOC_DBG_G_REGISTER:
- {
- u64 val;
- struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg;
- if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val;
- ret = pvr2_hdw_register_access(
- hdw, &req->match, req->reg,
- cmd == VIDIOC_DBG_S_REGISTER, &val);
- if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val;
- break;
- }
-#endif
+static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ u64 val;
+ int ret;
- default :
- ret = -ENOTTY;
- break;
- }
+ ret = pvr2_hdw_register_access(
+ hdw, &req->match, req->reg,
+ 0, &val);
+ req->val = val;
+ return ret;
+}
- pvr2_hdw_commit_ctl(hdw);
+static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ u64 val;
+ int ret;
- if (ret < 0) {
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl failure, ret=%ld", ret);
- } else {
- if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl failure, ret=%ld"
- " command was:", ret);
- v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
- cmd);
- }
- }
- } else {
- pvr2_trace(PVR2_TRACE_V4LIOCTL,
- "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
- ret, ret);
- }
+ val = req->val;
+ ret = pvr2_hdw_register_access(
+ hdw, &req->match, req->reg,
+ 1, &val);
return ret;
}
+#endif
+
+static const struct v4l2_ioctl_ops pvr2_ioctl_ops = {
+ .vidioc_querycap = pvr2_querycap,
+ .vidioc_g_priority = pvr2_g_priority,
+ .vidioc_s_priority = pvr2_s_priority,
+ .vidioc_s_audio = pvr2_s_audio,
+ .vidioc_g_audio = pvr2_g_audio,
+ .vidioc_enumaudio = pvr2_enumaudio,
+ .vidioc_enum_input = pvr2_enum_input,
+ .vidioc_cropcap = pvr2_cropcap,
+ .vidioc_s_crop = pvr2_s_crop,
+ .vidioc_g_crop = pvr2_g_crop,
+ .vidioc_g_input = pvr2_g_input,
+ .vidioc_s_input = pvr2_s_input,
+ .vidioc_g_frequency = pvr2_g_frequency,
+ .vidioc_s_frequency = pvr2_s_frequency,
+ .vidioc_s_tuner = pvr2_s_tuner,
+ .vidioc_g_tuner = pvr2_g_tuner,
+ .vidioc_g_std = pvr2_g_std,
+ .vidioc_s_std = pvr2_s_std,
+ .vidioc_querystd = pvr2_querystd,
+ .vidioc_log_status = pvr2_log_status,
+ .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap,
+ .vidioc_streamon = pvr2_streamon,
+ .vidioc_streamoff = pvr2_streamoff,
+ .vidioc_queryctrl = pvr2_queryctrl,
+ .vidioc_querymenu = pvr2_querymenu,
+ .vidioc_g_ctrl = pvr2_g_ctrl,
+ .vidioc_s_ctrl = pvr2_s_ctrl,
+ .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls,
+ .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls,
+ .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = pvr2_g_register,
+ .vidioc_s_register = pvr2_s_register,
+#endif
+};
static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
{
@@ -961,7 +959,56 @@ static long pvr2_v4l2_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
- return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl);
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ long ret = -EINVAL;
+
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL)
+ v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd);
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "ioctl failed - bad or no context");
+ return -EFAULT;
+ }
+
+ /* check priority */
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ ret = v4l2_prio_check(&vp->prio, fh->prio);
+ if (ret)
+ return ret;
+ }
+
+ ret = video_ioctl2(file, cmd, arg);
+
+ pvr2_hdw_commit_ctl(hdw);
+
+ if (ret < 0) {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%ld", ret);
+ } else {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%ld"
+ " command was:", ret);
+ v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
+ cmd);
+ }
+ }
+ } else {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)",
+ ret, ret);
+ }
+ return ret;
+
}
@@ -1262,10 +1309,12 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
struct usb_device *usbdev;
int mindevnum;
int unit_number;
+ struct pvr2_hdw *hdw;
int *nr_ptr = NULL;
dip->v4lp = vp;
- usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw);
+ hdw = vp->channel.mc_head->hdw;
+ usbdev = pvr2_hdw_get_dev(hdw);
dip->v4l_type = v4l_type;
switch (v4l_type) {
case VFL_TYPE_GRABBER:
@@ -1300,9 +1349,17 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template));
dip->devbase.release = pvr2_video_device_release;
+ dip->devbase.ioctl_ops = &pvr2_ioctl_ops;
+ {
+ int val;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,
+ PVR2_CID_STDAVAIL), &val);
+ dip->devbase.tvnorms = (v4l2_std_id)val;
+ }
mindevnum = -1;
- unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw);
+ unit_number = pvr2_hdw_get_unit_number(hdw);
if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) {
mindevnum = nr_ptr[unit_number];
}
@@ -1319,7 +1376,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
video_device_node_name(&dip->devbase),
pvr2_config_get_name(dip->config));
- pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,
+ pvr2_hdw_v4l_store_minor_number(hdw,
dip->minor_type,dip->devbase.minor);
}
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index 122fbd0081e..ec4e2ef54e6 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -357,6 +357,7 @@ handler_end:
PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
}
+/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
static int pwc_isoc_init(struct pwc_device *pdev)
{
struct usb_device *udev;
@@ -366,9 +367,6 @@ static int pwc_isoc_init(struct pwc_device *pdev)
struct usb_host_interface *idesc = NULL;
int compression = 0; /* 0..3 = uncompressed..high */
- if (pdev->iso_init)
- return 0;
-
pdev->vsync = 0;
pdev->vlast_packet_size = 0;
pdev->fill_buf = NULL;
@@ -418,7 +416,6 @@ retry:
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL);
if (urb == NULL) {
PWC_ERROR("Failed to allocate urb %d\n", i);
- pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
return -ENOMEM;
}
@@ -435,7 +432,6 @@ retry:
&urb->transfer_dma);
if (urb->transfer_buffer == NULL) {
PWC_ERROR("Failed to allocate urb buffer %d\n", i);
- pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
return -ENOMEM;
}
@@ -455,13 +451,11 @@ retry:
ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL);
if (ret == -ENOSPC && compression < 3) {
compression++;
- pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
goto retry;
}
if (ret) {
PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
- pdev->iso_init = 1;
pwc_isoc_cleanup(pdev);
return ret;
}
@@ -469,7 +463,6 @@ retry:
}
/* All is done... */
- pdev->iso_init = 1;
PWC_DEBUG_OPEN("<< pwc_isoc_init()\n");
return 0;
}
@@ -507,21 +500,19 @@ static void pwc_iso_free(struct pwc_device *pdev)
}
}
+/* Both v4l2_lock and vb_queue_lock should be locked when calling this */
static void pwc_isoc_cleanup(struct pwc_device *pdev)
{
PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
- if (pdev->iso_init == 0)
- return;
-
pwc_iso_stop(pdev);
pwc_iso_free(pdev);
usb_set_interface(pdev->udev, 0, 0);
- pdev->iso_init = 0;
PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n");
}
+/* Must be called with vb_queue_lock hold */
static void pwc_cleanup_queued_bufs(struct pwc_device *pdev)
{
unsigned long flags = 0;
@@ -573,18 +564,13 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type)
int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file)
{
- int r = 0;
-
- mutex_lock(&pdev->capt_file_lock);
if (pdev->capt_file != NULL &&
- pdev->capt_file != file) {
- r = -EBUSY;
- goto leave;
- }
+ pdev->capt_file != file)
+ return -EBUSY;
+
pdev->capt_file = file;
-leave:
- mutex_unlock(&pdev->capt_file_lock);
- return r;
+
+ return 0;
}
static void pwc_video_release(struct v4l2_device *v)
@@ -592,6 +578,7 @@ static void pwc_video_release(struct v4l2_device *v)
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
v4l2_ctrl_handler_free(&pdev->ctrl_handler);
+ v4l2_device_unregister(&pdev->v4l2_dev);
kfree(pdev->ctrl_buf);
kfree(pdev);
}
@@ -600,10 +587,25 @@ static int pwc_video_close(struct file *file)
{
struct pwc_device *pdev = video_drvdata(file);
+ /*
+ * If we're still streaming vb2_queue_release will call stream_stop
+ * so we must take both the v4l2_lock and the vb_queue_lock.
+ */
+ if (mutex_lock_interruptible(&pdev->v4l2_lock))
+ return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock)) {
+ mutex_unlock(&pdev->v4l2_lock);
+ return -ERESTARTSYS;
+ }
+
if (pdev->capt_file == file) {
vb2_queue_release(&pdev->vb_queue);
pdev->capt_file = NULL;
}
+
+ mutex_unlock(&pdev->vb_queue_lock);
+ mutex_unlock(&pdev->v4l2_lock);
+
return v4l2_fh_release(file);
}
@@ -611,35 +613,81 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct pwc_device *pdev = video_drvdata(file);
+ int lock_v4l2 = 0;
+ ssize_t ret;
- if (!pdev->udev)
- return -ENODEV;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- if (pwc_test_n_set_capt_file(pdev, file))
- return -EBUSY;
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret)
+ goto out;
- return vb2_read(&pdev->vb_queue, buf, count, ppos,
- file->f_flags & O_NONBLOCK);
+ /* stream_start will get called so we must take the v4l2_lock */
+ if (pdev->vb_queue.fileio == NULL)
+ lock_v4l2 = 1;
+
+ /* Use try_lock, since we're taking the locks in the *wrong* order! */
+ if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) {
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+ ret = vb2_read(&pdev->vb_queue, buf, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ if (lock_v4l2)
+ mutex_unlock(&pdev->v4l2_lock);
+out:
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static unsigned int pwc_video_poll(struct file *file, poll_table *wait)
{
struct pwc_device *pdev = video_drvdata(file);
+ struct vb2_queue *q = &pdev->vb_queue;
+ unsigned long req_events = poll_requested_events(wait);
+ unsigned int ret = POLL_ERR;
+ int lock_v4l2 = 0;
- if (!pdev->udev)
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
return POLL_ERR;
- return vb2_poll(&pdev->vb_queue, file, wait);
+ /* Will this start fileio and thus call start_stream? */
+ if ((req_events & (POLLIN | POLLRDNORM)) &&
+ q->num_buffers == 0 && !q->streaming && q->fileio == NULL) {
+ if (pwc_test_n_set_capt_file(pdev, file))
+ goto out;
+ lock_v4l2 = 1;
+ }
+
+ /* Use try_lock, since we're taking the locks in the *wrong* order! */
+ if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock))
+ goto out;
+ ret = vb2_poll(&pdev->vb_queue, file, wait);
+ if (lock_v4l2)
+ mutex_unlock(&pdev->v4l2_lock);
+
+out:
+ if (!pdev->udev)
+ ret |= POLLHUP;
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma)
{
struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- if (pdev->capt_file != file)
- return -EBUSY;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- return vb2_mmap(&pdev->vb_queue, vma);
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret == 0)
+ ret = vb2_mmap(&pdev->vb_queue, vma);
+
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
/***************************************************************************/
@@ -715,12 +763,14 @@ static void buffer_queue(struct vb2_buffer *vb)
struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb);
unsigned long flags = 0;
- spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
/* Check the device has not disconnected between prep and queuing */
- if (pdev->udev)
- list_add_tail(&buf->list, &pdev->queued_bufs);
- else
+ if (!pdev->udev) {
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ return;
+ }
+
+ spin_lock_irqsave(&pdev->queued_bufs_lock, flags);
+ list_add_tail(&buf->list, &pdev->queued_bufs);
spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags);
}
@@ -729,11 +779,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct pwc_device *pdev = vb2_get_drv_priv(vq);
int r;
- mutex_lock(&pdev->udevlock);
- if (!pdev->udev) {
- r = -ENODEV;
- goto leave;
- }
+ if (!pdev->udev)
+ return -ENODEV;
/* Turn on camera and set LEDS on */
pwc_camera_power(pdev, 1);
@@ -747,8 +794,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
/* And cleanup any queued bufs!! */
pwc_cleanup_queued_bufs(pdev);
}
-leave:
- mutex_unlock(&pdev->udevlock);
+
return r;
}
@@ -756,19 +802,29 @@ static int stop_streaming(struct vb2_queue *vq)
{
struct pwc_device *pdev = vb2_get_drv_priv(vq);
- mutex_lock(&pdev->udevlock);
if (pdev->udev) {
pwc_set_leds(pdev, 0, 0);
pwc_camera_power(pdev, 0);
pwc_isoc_cleanup(pdev);
}
- mutex_unlock(&pdev->udevlock);
pwc_cleanup_queued_bufs(pdev);
return 0;
}
+static void wait_prepare(struct vb2_queue *vq)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ mutex_unlock(&pdev->vb_queue_lock);
+}
+
+static void wait_finish(struct vb2_queue *vq)
+{
+ struct pwc_device *pdev = vb2_get_drv_priv(vq);
+ mutex_lock(&pdev->vb_queue_lock);
+}
+
static struct vb2_ops pwc_vb_queue_ops = {
.queue_setup = queue_setup,
.buf_init = buffer_init,
@@ -778,6 +834,8 @@ static struct vb2_ops pwc_vb_queue_ops = {
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
+ .wait_prepare = wait_prepare,
+ .wait_finish = wait_finish,
};
/***************************************************************************/
@@ -1057,8 +1115,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->features = features;
pwc_construct(pdev); /* set min/max sizes correct */
- mutex_init(&pdev->capt_file_lock);
- mutex_init(&pdev->udevlock);
+ mutex_init(&pdev->v4l2_lock);
+ mutex_init(&pdev->vb_queue_lock);
spin_lock_init(&pdev->queued_bufs_lock);
INIT_LIST_HEAD(&pdev->queued_bufs);
@@ -1130,6 +1188,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler;
pdev->vdev.v4l2_dev = &pdev->v4l2_dev;
+ pdev->vdev.lock = &pdev->v4l2_lock;
+
+ /*
+ * Don't take v4l2_lock for these ioctls. This improves latency if
+ * v4l2_lock is taken for a long time, e.g. when changing a control
+ * value, and a new frame is ready to be dequeued.
+ */
+ v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_DQBUF);
+ v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QBUF);
+ v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QUERYBUF);
rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1);
if (rc < 0) {
@@ -1185,16 +1253,20 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
struct v4l2_device *v = usb_get_intfdata(intf);
struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
- mutex_lock(&pdev->udevlock);
+ mutex_lock(&pdev->v4l2_lock);
+
+ mutex_lock(&pdev->vb_queue_lock);
/* No need to keep the urbs around after disconnection */
- pwc_isoc_cleanup(pdev);
+ if (pdev->vb_queue.streaming)
+ pwc_isoc_cleanup(pdev);
pdev->udev = NULL;
- mutex_unlock(&pdev->udevlock);
-
pwc_cleanup_queued_bufs(pdev);
+ mutex_unlock(&pdev->vb_queue_lock);
+ v4l2_device_disconnect(&pdev->v4l2_dev);
video_unregister_device(&pdev->vdev);
- v4l2_device_unregister(&pdev->v4l2_dev);
+
+ mutex_unlock(&pdev->v4l2_lock);
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
if (pdev->button_dev)
@@ -1229,15 +1301,4 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("pwcx");
MODULE_VERSION( PWC_VERSION );
-static int __init usb_pwc_init(void)
-{
- return usb_register(&pwc_driver);
-}
-
-static void __exit usb_pwc_exit(void)
-{
- usb_deregister(&pwc_driver);
-}
-
-module_init(usb_pwc_init);
-module_exit(usb_pwc_exit);
+module_usb_driver(pwc_driver);
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 2834e3e65b3..c691e29cc36 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -464,26 +464,24 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
struct pwc_device *pdev = video_drvdata(file);
int ret, pixelformat, compression = 0;
- if (pwc_test_n_set_capt_file(pdev, file))
- return -EBUSY;
-
ret = pwc_vidioc_try_fmt(pdev, f);
if (ret < 0)
return ret;
- pixelformat = f->fmt.pix.pixelformat;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- mutex_lock(&pdev->udevlock);
- if (!pdev->udev) {
- ret = -ENODEV;
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret)
goto leave;
- }
- if (pdev->iso_init) {
+ if (pdev->vb_queue.streaming) {
ret = -EBUSY;
goto leave;
}
+ pixelformat = f->fmt.pix.pixelformat;
+
PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d "
"format=%c%c%c%c\n",
f->fmt.pix.width, f->fmt.pix.height, pdev->vframes,
@@ -499,7 +497,7 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
leave:
- mutex_unlock(&pdev->udevlock);
+ mutex_unlock(&pdev->vb_queue_lock);
return ret;
}
@@ -507,9 +505,6 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap
{
struct pwc_device *pdev = video_drvdata(file);
- if (!pdev->udev)
- return -ENODEV;
-
strcpy(cap->driver, PWC_NAME);
strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card));
usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info));
@@ -540,15 +535,12 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i)
return i ? -EINVAL : 0;
}
-static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl)
+static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct pwc_device *pdev =
container_of(ctrl->handler, struct pwc_device, ctrl_handler);
int ret = 0;
- if (!pdev->udev)
- return -ENODEV;
-
switch (ctrl->id) {
case V4L2_CID_AUTO_WHITE_BALANCE:
if (pdev->color_bal_valid &&
@@ -615,18 +607,6 @@ static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl)
return ret;
}
-static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct pwc_device *pdev =
- container_of(ctrl->handler, struct pwc_device, ctrl_handler);
- int ret;
-
- mutex_lock(&pdev->udevlock);
- ret = pwc_g_volatile_ctrl_unlocked(ctrl);
- mutex_unlock(&pdev->udevlock);
- return ret;
-}
-
static int pwc_set_awb(struct pwc_device *pdev)
{
int ret;
@@ -648,7 +628,7 @@ static int pwc_set_awb(struct pwc_device *pdev)
if (pdev->auto_white_balance->val == awb_indoor ||
pdev->auto_white_balance->val == awb_outdoor ||
pdev->auto_white_balance->val == awb_fl)
- pwc_g_volatile_ctrl_unlocked(pdev->auto_white_balance);
+ pwc_g_volatile_ctrl(pdev->auto_white_balance);
}
if (pdev->auto_white_balance->val != awb_manual)
return 0;
@@ -812,13 +792,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
container_of(ctrl->handler, struct pwc_device, ctrl_handler);
int ret = 0;
- mutex_lock(&pdev->udevlock);
-
- if (!pdev->udev) {
- ret = -ENODEV;
- goto leave;
- }
-
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL,
@@ -915,8 +888,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl)
if (ret)
PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
-leave:
- mutex_unlock(&pdev->udevlock);
return ret;
}
@@ -949,11 +920,9 @@ static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */
PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",
pdev->width, pdev->height);
pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt);
- mutex_unlock(&pdev->udevlock);
return 0;
}
@@ -968,70 +937,98 @@ static int pwc_reqbufs(struct file *file, void *fh,
struct v4l2_requestbuffers *rb)
{
struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- if (pwc_test_n_set_capt_file(pdev, file))
- return -EBUSY;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- return vb2_reqbufs(&pdev->vb_queue, rb);
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret == 0)
+ ret = vb2_reqbufs(&pdev->vb_queue, rb);
+
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- return vb2_querybuf(&pdev->vb_queue, buf);
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
+
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret == 0)
+ ret = vb2_querybuf(&pdev->vb_queue, buf);
+
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- if (!pdev->udev)
- return -ENODEV;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- if (pdev->capt_file != file)
- return -EBUSY;
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret == 0)
+ ret = vb2_qbuf(&pdev->vb_queue, buf);
- return vb2_qbuf(&pdev->vb_queue, buf);
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
{
struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- if (!pdev->udev)
- return -ENODEV;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- if (pdev->capt_file != file)
- return -EBUSY;
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret == 0)
+ ret = vb2_dqbuf(&pdev->vb_queue, buf,
+ file->f_flags & O_NONBLOCK);
- return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK);
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- if (!pdev->udev)
- return -ENODEV;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- if (pdev->capt_file != file)
- return -EBUSY;
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret == 0)
+ ret = vb2_streamon(&pdev->vb_queue, i);
- return vb2_streamon(&pdev->vb_queue, i);
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i)
{
struct pwc_device *pdev = video_drvdata(file);
+ int ret;
- if (!pdev->udev)
- return -ENODEV;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
- if (pdev->capt_file != file)
- return -EBUSY;
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret == 0)
+ ret = vb2_streamoff(&pdev->vb_queue, i);
- return vb2_streamoff(&pdev->vb_queue, i);
+ mutex_unlock(&pdev->vb_queue_lock);
+ return ret;
}
static int pwc_enum_framesizes(struct file *file, void *fh,
@@ -1119,19 +1116,17 @@ static int pwc_s_parm(struct file *file, void *fh,
parm->parm.capture.timeperframe.numerator == 0)
return -EINVAL;
- if (pwc_test_n_set_capt_file(pdev, file))
- return -EBUSY;
-
fps = parm->parm.capture.timeperframe.denominator /
parm->parm.capture.timeperframe.numerator;
- mutex_lock(&pdev->udevlock);
- if (!pdev->udev) {
- ret = -ENODEV;
+ if (mutex_lock_interruptible(&pdev->vb_queue_lock))
+ return -ERESTARTSYS;
+
+ ret = pwc_test_n_set_capt_file(pdev, file);
+ if (ret)
goto leave;
- }
- if (pdev->iso_init) {
+ if (pdev->vb_queue.streaming) {
ret = -EBUSY;
goto leave;
}
@@ -1142,7 +1137,7 @@ static int pwc_s_parm(struct file *file, void *fh,
pwc_g_parm(file, fh, parm);
leave:
- mutex_unlock(&pdev->udevlock);
+ mutex_unlock(&pdev->vb_queue_lock);
return ret;
}
@@ -1166,4 +1161,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = {
.vidioc_enum_frameintervals = pwc_enum_frameintervals,
.vidioc_g_parm = pwc_g_parm,
.vidioc_s_parm = pwc_s_parm,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
index e4d4d711dd1..d6b5b216b9d 100644
--- a/drivers/media/video/pwc/pwc.h
+++ b/drivers/media/video/pwc/pwc.h
@@ -221,9 +221,17 @@ struct pwc_device
struct video_device vdev;
struct v4l2_device v4l2_dev;
- /* Pointer to our usb_device, may be NULL after unplug */
- struct usb_device *udev;
- struct mutex udevlock;
+ /* videobuf2 queue and queued buffers list */
+ struct vb2_queue vb_queue;
+ struct list_head queued_bufs;
+ spinlock_t queued_bufs_lock; /* Protects queued_bufs */
+
+ /* Note if taking both locks v4l2_lock must always be locked first! */
+ struct mutex v4l2_lock; /* Protects everything else */
+ struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */
+
+ /* Pointer to our usb_device, will be NULL after unplug */
+ struct usb_device *udev; /* Both mutexes most be hold when setting! */
/* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */
int type;
@@ -232,7 +240,6 @@ struct pwc_device
/*** Video data ***/
struct file *capt_file; /* file doing video capture */
- struct mutex capt_file_lock;
int vendpoint; /* video isoc endpoint */
int vcinterface; /* video control interface */
int valternate; /* alternate interface needed */
@@ -251,12 +258,6 @@ struct pwc_device
unsigned char *ctrl_buf;
struct urb *urbs[MAX_ISO_BUFS];
- char iso_init;
-
- /* videobuf2 queue and queued buffers list */
- struct vb2_queue vb_queue;
- struct list_head queued_bufs;
- spinlock_t queued_bufs_lock;
/*
* Frame currently being filled, this only gets touched by the
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index 5a413f4427e..9c21e01f2c2 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -241,15 +241,10 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
unsigned int *size)
{
struct soc_camera_device *icd = vq->priv_data;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
- *size = bytes_per_line * icd->user_height;
+ *size = icd->sizeimage;
if (0 == *count)
*count = 32;
@@ -435,11 +430,6 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
int ret;
int size_y, size_u = 0, size_v = 0;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
-
- if (bytes_per_line < 0)
- return bytes_per_line;
dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
@@ -474,7 +464,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
vb->state = VIDEOBUF_NEEDS_INIT;
}
- vb->size = bytes_per_line * vb->height;
+ vb->size = icd->sizeimage;
if (0 != vb->baddr && vb->bsize < vb->size) {
ret = -EINVAL;
goto out;
@@ -1244,6 +1234,7 @@ static const struct soc_mbus_pixelfmt pxa_camera_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_U_V,
},
};
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index 4894cbb1c54..01c2179f052 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -634,13 +634,11 @@ static void s2255_fillbuff(struct s2255_channel *channel,
const char *tmpbuf;
char *vbuf = videobuf_to_vmalloc(&buf->vb);
unsigned long last_frame;
- struct s2255_framei *frm;
if (!vbuf)
return;
last_frame = channel->last_frame;
if (last_frame != -1) {
- frm = &channel->buffer.frame[last_frame];
tmpbuf =
(const char *)channel->buffer.frame[last_frame].lpvbits;
switch (buf->fmt->fourcc) {
@@ -987,7 +985,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct videobuf_queue *q = &fh->vb_vidq;
struct s2255_mode mode;
int ret;
- int norm;
ret = vidioc_try_fmt_vid_cap(file, fh, f);
@@ -1018,7 +1015,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
channel->height = f->fmt.pix.height;
fh->vb_vidq.field = f->fmt.pix.field;
fh->type = f->type;
- norm = norm_minw(&channel->vdev);
if (channel->width > norm_minw(&channel->vdev)) {
if (channel->height > norm_minh(&channel->vdev)) {
if (channel->cap_parm.capturemode &
@@ -1826,8 +1822,7 @@ static void s2255_destroy(struct s2255_dev *dev)
usb_free_urb(dev->fw_data->fw_urb);
dev->fw_data->fw_urb = NULL;
}
- if (dev->fw_data->fw)
- release_firmware(dev->fw_data->fw);
+ release_firmware(dev->fw_data->fw);
kfree(dev->fw_data->pfw_data);
kfree(dev->fw_data);
/* reset the DSP so firmware can be reloaded next time */
@@ -1949,6 +1944,10 @@ static int s2255_probe_v4l(struct s2255_dev *dev)
/* register 4 video devices */
channel->vdev = template;
channel->vdev.lock = &dev->lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &channel->vdev.flags);
channel->vdev.v4l2_dev = &dev->v4l2_dev;
video_set_drvdata(&channel->vdev, channel);
if (video_nr == -1)
diff --git a/drivers/media/video/s5p-fimc/Kconfig b/drivers/media/video/s5p-fimc/Kconfig
new file mode 100644
index 00000000000..a564f7eeb06
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/Kconfig
@@ -0,0 +1,48 @@
+
+config VIDEO_SAMSUNG_S5P_FIMC
+ bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME
+ depends on EXPERIMENTAL
+ help
+ Say Y here to enable camera host interface devices for
+ Samsung S5P and EXYNOS SoC series.
+
+if VIDEO_SAMSUNG_S5P_FIMC
+
+config VIDEO_S5P_FIMC
+ tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver"
+ depends on I2C
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host
+ interface and video postprocessor (FIMC and FIMC-LITE) devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5p-fimc.
+
+config VIDEO_S5P_MIPI_CSIS
+ tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
+ depends on REGULATOR
+ help
+ This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
+ receiver (MIPI-CSIS) devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called s5p-csis.
+
+if ARCH_EXYNOS
+
+config VIDEO_EXYNOS_FIMC_LITE
+ tristate "EXYNOS FIMC-LITE camera interface driver"
+ depends on I2C
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera
+ host interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called exynos-fimc-lite.
+endif
+
+endif # VIDEO_SAMSUNG_S5P_FIMC
diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile
index 33dec7f890e..46485143e1c 100644
--- a/drivers/media/video/s5p-fimc/Makefile
+++ b/drivers/media/video/s5p-fimc/Makefile
@@ -1,5 +1,7 @@
-s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o
+s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o
+exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o
s5p-csis-objs := mipi-csis.o
obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc.o
+obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o
+obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index 7e9b2c612b0..35457459190 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -1,8 +1,8 @@
/*
* Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver
*
- * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd.
- * Author: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -29,20 +29,22 @@
#include "fimc-mdevice.h"
#include "fimc-core.h"
+#include "fimc-reg.h"
-static int fimc_init_capture(struct fimc_dev *fimc)
+static int fimc_capture_hw_init(struct fimc_dev *fimc)
{
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_pipeline *p = &fimc->pipeline;
struct fimc_sensor_info *sensor;
unsigned long flags;
int ret = 0;
- if (fimc->pipeline.sensor == NULL || ctx == NULL)
+ if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL)
return -ENXIO;
if (ctx->s_frame.fmt == NULL)
return -EINVAL;
- sensor = v4l2_get_subdev_hostdata(fimc->pipeline.sensor);
+ sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]);
spin_lock_irqsave(&fimc->slock, flags);
fimc_prepare_dma_offset(ctx, &ctx->d_frame);
@@ -60,7 +62,7 @@ static int fimc_init_capture(struct fimc_dev *fimc)
fimc_hw_set_mainscaler(ctx);
fimc_hw_set_target_format(ctx);
fimc_hw_set_rotation(ctx);
- fimc_hw_set_effect(ctx, false);
+ fimc_hw_set_effect(ctx);
fimc_hw_set_output_path(ctx);
fimc_hw_set_out_dma(ctx);
if (fimc->variant->has_alpha)
@@ -71,6 +73,14 @@ static int fimc_init_capture(struct fimc_dev *fimc)
return ret;
}
+/*
+ * Reinitialize the driver so it is ready to start the streaming again.
+ * Set fimc->state to indicate stream off and the hardware shut down state.
+ * If not suspending (@suspend is false), return any buffers to videobuf2.
+ * Otherwise put any owned buffers onto the pending buffers queue, so they
+ * can be re-spun when the device is being resumed. Also perform FIMC
+ * software reset and disable streaming on the whole pipeline if required.
+ */
static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
{
struct fimc_vid_cap *cap = &fimc->vid_cap;
@@ -83,7 +93,9 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT |
1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM);
- if (!suspend)
+ if (suspend)
+ fimc->state |= (1 << ST_CAPT_SUSPENDED);
+ else
fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED);
/* Release unused buffers */
@@ -99,7 +111,6 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
else
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
- set_bit(ST_CAPT_SUSPENDED, &fimc->state);
fimc_hw_reset(fimc);
cap->buf_index = 0;
@@ -107,7 +118,7 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend)
spin_unlock_irqrestore(&fimc->slock, flags);
if (streaming)
- return fimc_pipeline_s_stream(fimc, 0);
+ return fimc_pipeline_s_stream(&fimc->pipeline, 0);
else
return 0;
}
@@ -138,32 +149,96 @@ static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend)
* spinlock held. It updates the camera pixel crop, rotation and
* image flip in H/W.
*/
-int fimc_capture_config_update(struct fimc_ctx *ctx)
+static int fimc_capture_config_update(struct fimc_ctx *ctx)
{
struct fimc_dev *fimc = ctx->fimc_dev;
int ret;
- if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
- return 0;
-
- spin_lock(&ctx->slock);
fimc_hw_set_camera_offset(fimc, &ctx->s_frame);
+
ret = fimc_set_scaler_info(ctx);
- if (ret == 0) {
- fimc_hw_set_prescaler(ctx);
- fimc_hw_set_mainscaler(ctx);
- fimc_hw_set_target_format(ctx);
- fimc_hw_set_rotation(ctx);
- fimc_prepare_dma_offset(ctx, &ctx->d_frame);
- fimc_hw_set_out_dma(ctx);
- if (fimc->variant->has_alpha)
- fimc_hw_set_rgb_alpha(ctx);
- clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
- }
- spin_unlock(&ctx->slock);
+ if (ret)
+ return ret;
+
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx);
+ fimc_prepare_dma_offset(ctx, &ctx->d_frame);
+ fimc_hw_set_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+
+ clear_bit(ST_CAPT_APPLY_CFG, &fimc->state);
return ret;
}
+void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf)
+{
+ struct fimc_vid_cap *cap = &fimc->vid_cap;
+ struct fimc_vid_buffer *v_buf;
+ struct timeval *tv;
+ struct timespec ts;
+
+ if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ goto done;
+ }
+
+ if (!list_empty(&cap->active_buf_q) &&
+ test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) {
+ ktime_get_real_ts(&ts);
+
+ v_buf = fimc_active_queue_pop(cap);
+
+ tv = &v_buf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ v_buf->vb.v4l2_buf.sequence = cap->frame_count++;
+
+ vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
+ }
+
+ if (!list_empty(&cap->pending_buf_q)) {
+
+ v_buf = fimc_pending_queue_pop(cap);
+ fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
+ v_buf->index = cap->buf_index;
+
+ /* Move the buffer to the capture active queue */
+ fimc_active_queue_add(cap, v_buf);
+
+ dbg("next frame: %d, done frame: %d",
+ fimc_hw_get_frame_index(fimc), v_buf->index);
+
+ if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ cap->buf_index = 0;
+ }
+
+ if (cap->active_buf_cnt == 0) {
+ if (deq_buf)
+ clear_bit(ST_CAPT_RUN, &fimc->state);
+
+ if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
+ cap->buf_index = 0;
+ } else {
+ set_bit(ST_CAPT_RUN, &fimc->state);
+ }
+
+ if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state))
+ fimc_capture_config_update(cap->ctx);
+done:
+ if (cap->active_buf_cnt == 1) {
+ fimc_deactivate_capture(fimc);
+ clear_bit(ST_CAPT_STREAM, &fimc->state);
+ }
+
+ dbg("frame: %d, active_buf_cnt: %d",
+ fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
+}
+
+
static int start_streaming(struct vb2_queue *q, unsigned int count)
{
struct fimc_ctx *ctx = q->drv_priv;
@@ -174,9 +249,11 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
vid_cap->frame_count = 0;
- ret = fimc_init_capture(fimc);
- if (ret)
- goto error;
+ ret = fimc_capture_hw_init(fimc);
+ if (ret) {
+ fimc_capture_state_cleanup(fimc, false);
+ return ret;
+ }
set_bit(ST_CAPT_PEND, &fimc->state);
@@ -187,13 +264,10 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
fimc_activate_capture(ctx);
if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
- fimc_pipeline_s_stream(fimc, 1);
+ fimc_pipeline_s_stream(&fimc->pipeline, 1);
}
return 0;
-error:
- fimc_capture_state_cleanup(fimc, false);
- return ret;
}
static int stop_streaming(struct vb2_queue *q)
@@ -214,7 +288,7 @@ int fimc_capture_suspend(struct fimc_dev *fimc)
int ret = fimc_stop_capture(fimc, suspend);
if (ret)
return ret;
- return fimc_pipeline_shutdown(fimc);
+ return fimc_pipeline_shutdown(&fimc->pipeline);
}
static void buffer_queue(struct vb2_buffer *vb);
@@ -230,9 +304,9 @@ int fimc_capture_resume(struct fimc_dev *fimc)
INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q);
vid_cap->buf_index = 0;
- fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity,
+ fimc_pipeline_initialize(&fimc->pipeline, &vid_cap->vfd->entity,
false);
- fimc_init_capture(fimc);
+ fimc_capture_hw_init(fimc);
clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
@@ -347,7 +421,7 @@ static void buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&fimc->slock, flags);
if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state))
- fimc_pipeline_s_stream(fimc, 1);
+ fimc_pipeline_s_stream(&fimc->pipeline, 1);
return;
}
spin_unlock_irqrestore(&fimc->slock, flags);
@@ -389,15 +463,15 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc)
if (WARN_ON(vid_cap->ctx == NULL))
return -ENXIO;
- if (vid_cap->ctx->ctrls_rdy)
+ if (vid_cap->ctx->ctrls.ready)
return 0;
ret = fimc_ctrls_create(vid_cap->ctx);
- if (ret || vid_cap->user_subdev_api)
+ if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready)
return ret;
- return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler,
- fimc->pipeline.sensor->ctrl_handler);
+ return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler,
+ fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler);
}
static int fimc_capture_set_default_format(struct fimc_dev *fimc);
@@ -420,7 +494,7 @@ static int fimc_capture_open(struct file *file)
pm_runtime_get_sync(&fimc->pdev->dev);
if (++fimc->vid_cap.refcnt == 1) {
- ret = fimc_pipeline_initialize(fimc,
+ ret = fimc_pipeline_initialize(&fimc->pipeline,
&fimc->vid_cap.vfd->entity, true);
if (ret < 0) {
dev_err(&fimc->pdev->dev,
@@ -448,7 +522,7 @@ static int fimc_capture_close(struct file *file)
if (--fimc->vid_cap.refcnt == 0) {
clear_bit(ST_CAPT_BUSY, &fimc->state);
fimc_stop_capture(fimc, false);
- fimc_pipeline_shutdown(fimc);
+ fimc_pipeline_shutdown(&fimc->pipeline);
clear_bit(ST_CAPT_SUSPENDED, &fimc->state);
}
@@ -495,7 +569,7 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
{
bool rotation = ctx->rotation == 90 || ctx->rotation == 270;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_variant *var = fimc->variant;
struct fimc_pix_limit *pl = var->pix_limit;
struct fimc_frame *dst = &ctx->d_frame;
u32 depth, min_w, max_w, min_h, align_h = 3;
@@ -537,8 +611,13 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
}
/* Apply the scaler and the output DMA constraints */
max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w;
- min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize;
- min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize;
+ if (ctx->state & FIMC_COMPOSE) {
+ min_w = dst->offs_h + dst->width;
+ min_h = dst->offs_v + dst->height;
+ } else {
+ min_w = var->min_out_pixsize;
+ min_h = var->min_out_pixsize;
+ }
if (var->min_vsize_align == 1 && !rotation)
align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1;
@@ -556,12 +635,13 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx,
return ffmt;
}
-static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
- int pad)
+static void fimc_capture_try_selection(struct fimc_ctx *ctx,
+ struct v4l2_rect *r,
+ int target)
{
bool rotate = ctx->rotation == 90 || ctx->rotation == 270;
struct fimc_dev *fimc = ctx->fimc_dev;
- struct samsung_fimc_variant *var = fimc->variant;
+ struct fimc_variant *var = fimc->variant;
struct fimc_pix_limit *pl = var->pix_limit;
struct fimc_frame *sink = &ctx->s_frame;
u32 max_w, max_h, min_w = 0, min_h = 0, min_sz;
@@ -575,7 +655,7 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
r->left = r->top = 0;
return;
}
- if (pad == FIMC_SD_PAD_SOURCE) {
+ if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
if (ctx->rotation != 90 && ctx->rotation != 270)
align_h = 1;
max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3));
@@ -589,8 +669,7 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
max_sc_h = max_sc_v = 1;
}
/*
- * For the crop rectangle at source pad the following constraints
- * must be met:
+ * For the compose rectangle the following constraints must be met:
* - it must fit in the sink pad format rectangle (f_width/f_height);
* - maximum downscaling ratio is 64;
* - maximum crop size depends if the rotator is used or not;
@@ -602,7 +681,8 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
rotate ? pl->out_rot_en_w : pl->out_rot_dis_w,
rotate ? sink->f_height : sink->f_width);
max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height);
- if (pad == FIMC_SD_PAD_SOURCE) {
+
+ if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) {
min_w = min_t(u32, max_w, sink->f_width / max_sc_h);
min_h = min_t(u32, max_h, sink->f_height / max_sc_v);
if (rotate) {
@@ -613,13 +693,13 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r,
v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1,
&r->height, min_h, max_h, align_h,
align_sz);
- /* Adjust left/top if cropping rectangle is out of bounds */
+ /* Adjust left/top if crop/compose rectangle is out of bounds */
r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width);
r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
r->left = round_down(r->left, var->hor_offs_align);
- dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d",
- pad, r->left, r->top, r->width, r->height,
+ dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d",
+ target, r->left, r->top, r->width, r->height,
sink->f_width, sink->f_height);
}
@@ -669,8 +749,8 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
bool set)
{
struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_subdev *sd = fimc->pipeline.sensor;
- struct v4l2_subdev *csis = fimc->pipeline.csis;
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS];
struct v4l2_subdev_format sfmt;
struct v4l2_mbus_framefmt *mf = &sfmt.format;
struct fimc_fmt *ffmt = NULL;
@@ -851,7 +931,7 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f)
set_frame_bounds(ff, pix->width, pix->height);
/* Reset the composition rectangle if not yet configured */
- if (!(ctx->state & FIMC_DST_CROP))
+ if (!(ctx->state & FIMC_COMPOSE))
set_frame_crop(ff, 0, 0, pix->width, pix->height);
fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color));
@@ -878,7 +958,7 @@ static int fimc_cap_enum_input(struct file *file, void *priv,
struct v4l2_input *i)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
if (i->index != 0)
return -EINVAL;
@@ -927,7 +1007,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc)
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
/* Don't call FIMC subdev operation to avoid nested locking */
- if (sd == fimc->vid_cap.subdev) {
+ if (sd == &fimc->vid_cap.subdev) {
struct fimc_frame *ff = &vid_cap->ctx->s_frame;
sink_fmt.format.width = ff->f_width;
sink_fmt.format.height = ff->f_height;
@@ -970,7 +1050,8 @@ static int fimc_cap_streamon(struct file *file, void *priv,
if (fimc_capture_active(fimc))
return -EBUSY;
- media_entity_pipeline_start(&p->sensor->entity, p->pipe);
+ media_entity_pipeline_start(&p->subdevs[IDX_SENSOR]->entity,
+ p->m_pipeline);
if (fimc->vid_cap.user_subdev_api) {
ret = fimc_pipeline_validate(fimc);
@@ -984,7 +1065,7 @@ static int fimc_cap_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct v4l2_subdev *sd = fimc->pipeline.sensor;
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
int ret;
ret = vb2_streamoff(&fimc->vid_cap.vbq, type);
@@ -1100,29 +1181,18 @@ static int fimc_cap_s_selection(struct file *file, void *fh,
struct v4l2_rect rect = s->r;
struct fimc_frame *f;
unsigned long flags;
- unsigned int pad;
if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
return -EINVAL;
- switch (s->target) {
- case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- case V4L2_SEL_TGT_COMPOSE_BOUNDS:
- case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE)
f = &ctx->d_frame;
- pad = FIMC_SD_PAD_SOURCE;
- break;
- case V4L2_SEL_TGT_CROP_BOUNDS:
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_ACTIVE:
+ else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE)
f = &ctx->s_frame;
- pad = FIMC_SD_PAD_SINK;
- break;
- default:
+ else
return -EINVAL;
- }
- fimc_capture_try_crop(ctx, &rect, pad);
+ fimc_capture_try_selection(ctx, &rect, s->target);
if (s->flags & V4L2_SEL_FLAG_LE &&
!enclosed_rectangle(&rect, &s->r))
@@ -1243,7 +1313,7 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
struct fimc_vid_buffer, list);
vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg));
}
- fimc_capture_irq_handler(fimc, true);
+ fimc_capture_irq_handler(fimc, 1);
fimc_deactivate_capture(fimc);
spin_unlock_irqrestore(&fimc->slock, irq_flags);
}
@@ -1334,77 +1404,122 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
ff->fmt = ffmt;
/* Reset the crop rectangle if required. */
- if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP)))
+ if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE)))
set_frame_crop(ff, 0, 0, mf->width, mf->height);
if (fmt->pad == FIMC_SD_PAD_SINK)
- ctx->state &= ~FIMC_DST_CROP;
+ ctx->state &= ~FIMC_COMPOSE;
mutex_unlock(&fimc->lock);
return 0;
}
-static int fimc_subdev_get_crop(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
+static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_rect *r = &crop->rect;
- struct fimc_frame *ff;
+ struct fimc_frame *f = &ctx->s_frame;
+ struct v4l2_rect *r = &sel->r;
+ struct v4l2_rect *try_sel;
- if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
- crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad);
+ if (sel->pad != FIMC_SD_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&fimc->lock);
+
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ r->width = f->o_width;
+ r->height = f->o_height;
+ r->left = 0;
+ r->top = 0;
+ mutex_unlock(&fimc->lock);
return 0;
+
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
+ break;
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
+ f = &ctx->d_frame;
+ break;
+ default:
+ mutex_unlock(&fimc->lock);
+ return -EINVAL;
}
- ff = crop->pad == FIMC_SD_PAD_SINK ?
- &ctx->s_frame : &ctx->d_frame;
- mutex_lock(&fimc->lock);
- r->left = ff->offs_h;
- r->top = ff->offs_v;
- r->width = ff->width;
- r->height = ff->height;
- mutex_unlock(&fimc->lock);
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ sel->r = *try_sel;
+ } else {
+ r->left = f->offs_h;
+ r->top = f->offs_v;
+ r->width = f->width;
+ r->height = f->height;
+ }
- dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
- ff, crop->pad, r->left, r->top, r->width, r->height,
- ff->f_width, ff->f_height);
+ dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
+ sel->pad, r->left, r->top, r->width, r->height,
+ f->f_width, f->f_height);
+ mutex_unlock(&fimc->lock);
return 0;
}
-static int fimc_subdev_set_crop(struct v4l2_subdev *sd,
- struct v4l2_subdev_fh *fh,
- struct v4l2_subdev_crop *crop)
+static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
{
struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct v4l2_rect *r = &crop->rect;
- struct fimc_frame *ff;
+ struct fimc_frame *f = &ctx->s_frame;
+ struct v4l2_rect *r = &sel->r;
+ struct v4l2_rect *try_sel;
unsigned long flags;
- dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height);
-
- ff = crop->pad == FIMC_SD_PAD_SOURCE ?
- &ctx->d_frame : &ctx->s_frame;
+ if (sel->pad != FIMC_SD_PAD_SINK)
+ return -EINVAL;
mutex_lock(&fimc->lock);
- fimc_capture_try_crop(ctx, r, crop->pad);
+ fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE);
- if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ r->width = f->o_width;
+ r->height = f->o_height;
+ r->left = 0;
+ r->top = 0;
mutex_unlock(&fimc->lock);
- *v4l2_subdev_get_try_crop(fh, crop->pad) = *r;
return 0;
+
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ try_sel = v4l2_subdev_get_try_crop(fh, sel->pad);
+ break;
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ try_sel = v4l2_subdev_get_try_compose(fh, sel->pad);
+ f = &ctx->d_frame;
+ break;
+ default:
+ mutex_unlock(&fimc->lock);
+ return -EINVAL;
}
- spin_lock_irqsave(&fimc->slock, flags);
- set_frame_crop(ff, r->left, r->top, r->width, r->height);
- if (crop->pad == FIMC_SD_PAD_SOURCE)
- ctx->state |= FIMC_DST_CROP;
- set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
- spin_unlock_irqrestore(&fimc->slock, flags);
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *try_sel = sel->r;
+ } else {
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_frame_crop(f, r->left, r->top, r->width, r->height);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL)
+ ctx->state |= FIMC_COMPOSE;
+ }
- dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top,
+ dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top,
r->width, r->height);
mutex_unlock(&fimc->lock);
@@ -1413,63 +1528,16 @@ static int fimc_subdev_set_crop(struct v4l2_subdev *sd,
static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = {
.enum_mbus_code = fimc_subdev_enum_mbus_code,
+ .get_selection = fimc_subdev_get_selection,
+ .set_selection = fimc_subdev_set_selection,
.get_fmt = fimc_subdev_get_fmt,
.set_fmt = fimc_subdev_set_fmt,
- .get_crop = fimc_subdev_get_crop,
- .set_crop = fimc_subdev_set_crop,
};
static struct v4l2_subdev_ops fimc_subdev_ops = {
.pad = &fimc_subdev_pad_ops,
};
-static int fimc_create_capture_subdev(struct fimc_dev *fimc,
- struct v4l2_device *v4l2_dev)
-{
- struct v4l2_subdev *sd;
- int ret;
-
- sd = kzalloc(sizeof(*sd), GFP_KERNEL);
- if (!sd)
- return -ENOMEM;
-
- v4l2_subdev_init(sd, &fimc_subdev_ops);
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
- snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
-
- fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
- fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
- fimc->vid_cap.sd_pads, 0);
- if (ret)
- goto me_err;
- ret = v4l2_device_register_subdev(v4l2_dev, sd);
- if (ret)
- goto sd_err;
-
- fimc->vid_cap.subdev = sd;
- v4l2_set_subdevdata(sd, fimc);
- sd->entity.ops = &fimc_sd_media_ops;
- return 0;
-sd_err:
- media_entity_cleanup(&sd->entity);
-me_err:
- kfree(sd);
- return ret;
-}
-
-static void fimc_destroy_capture_subdev(struct fimc_dev *fimc)
-{
- struct v4l2_subdev *sd = fimc->vid_cap.subdev;
-
- if (!sd)
- return;
- media_entity_cleanup(&sd->entity);
- v4l2_device_unregister_subdev(sd);
- kfree(sd);
- fimc->vid_cap.subdev = NULL;
-}
-
/* Set default format at the sensor and host interface */
static int fimc_capture_set_default_format(struct fimc_dev *fimc)
{
@@ -1488,7 +1556,7 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc)
}
/* fimc->lock must be already initialized */
-int fimc_register_capture_device(struct fimc_dev *fimc,
+static int fimc_register_capture_device(struct fimc_dev *fimc,
struct v4l2_device *v4l2_dev)
{
struct video_device *vfd;
@@ -1502,11 +1570,11 @@ int fimc_register_capture_device(struct fimc_dev *fimc,
return -ENOMEM;
ctx->fimc_dev = fimc;
- ctx->in_path = FIMC_CAMERA;
- ctx->out_path = FIMC_DMA;
+ ctx->in_path = FIMC_IO_CAMERA;
+ ctx->out_path = FIMC_IO_DMA;
ctx->state = FIMC_CTX_CAP;
ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
- ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0);
+ ctx->d_frame.fmt = ctx->s_frame.fmt;
vfd = video_device_alloc();
if (!vfd) {
@@ -1514,8 +1582,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc,
goto err_vd_alloc;
}
- snprintf(vfd->name, sizeof(vfd->name), "%s.capture",
- dev_name(&fimc->pdev->dev));
+ snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.capture", fimc->id);
vfd->fops = &fimc_capture_fops;
vfd->ioctl_ops = &fimc_capture_ioctl_ops;
@@ -1523,6 +1590,10 @@ int fimc_register_capture_device(struct fimc_dev *fimc,
vfd->minor = -1;
vfd->release = video_device_release;
vfd->lock = &fimc->lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
video_set_drvdata(vfd, fimc);
vid_cap = &fimc->vid_cap;
@@ -1533,7 +1604,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc,
INIT_LIST_HEAD(&vid_cap->pending_buf_q);
INIT_LIST_HEAD(&vid_cap->active_buf_q);
- spin_lock_init(&ctx->slock);
vid_cap->ctx = ctx;
q = &fimc->vid_cap.vbq;
@@ -1547,18 +1617,22 @@ int fimc_register_capture_device(struct fimc_dev *fimc,
vb2_queue_init(q);
- fimc->vid_cap.vd_pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0);
+ vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0);
if (ret)
goto err_ent;
- ret = fimc_create_capture_subdev(fimc, v4l2_dev);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
if (ret)
- goto err_sd_reg;
+ goto err_vd;
- vfd->ctrl_handler = &ctx->ctrl_handler;
+ v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+
+ vfd->ctrl_handler = &ctx->ctrls.handler;
return 0;
-err_sd_reg:
+err_vd:
media_entity_cleanup(&vfd->entity);
err_ent:
video_device_release(vfd);
@@ -1567,17 +1641,73 @@ err_vd_alloc:
return ret;
}
-void fimc_unregister_capture_device(struct fimc_dev *fimc)
+static int fimc_capture_subdev_registered(struct v4l2_subdev *sd)
{
- struct video_device *vfd = fimc->vid_cap.vfd;
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = fimc_register_m2m_device(fimc, sd->v4l2_dev);
+ if (ret)
+ return ret;
- if (vfd) {
- media_entity_cleanup(&vfd->entity);
- /* Can also be called if video device was
- not registered */
- video_unregister_device(vfd);
+ ret = fimc_register_capture_device(fimc, sd->v4l2_dev);
+ if (ret)
+ fimc_unregister_m2m_device(fimc);
+
+ return ret;
+}
+
+static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_dev *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc == NULL)
+ return;
+
+ fimc_unregister_m2m_device(fimc);
+
+ if (fimc->vid_cap.vfd) {
+ media_entity_cleanup(&fimc->vid_cap.vfd->entity);
+ video_unregister_device(fimc->vid_cap.vfd);
+ fimc->vid_cap.vfd = NULL;
}
- fimc_destroy_capture_subdev(fimc);
+
kfree(fimc->vid_cap.ctx);
fimc->vid_cap.ctx = NULL;
}
+
+static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = {
+ .registered = fimc_capture_subdev_registered,
+ .unregistered = fimc_capture_subdev_unregistered,
+};
+
+int fimc_initialize_capture_subdev(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
+ int ret;
+
+ v4l2_subdev_init(sd, &fimc_subdev_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id);
+
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->vid_cap.sd_pads, 0);
+ if (ret)
+ return ret;
+
+ sd->entity.ops = &fimc_sd_media_ops;
+ sd->internal_ops = &fimc_capture_sd_internal_ops;
+ v4l2_set_subdevdata(sd, fimc);
+ return 0;
+}
+
+void fimc_unregister_capture_subdev(struct fimc_dev *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_set_subdevdata(sd, NULL);
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index e09ba7b0076..fedcd561ba2 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -1,8 +1,8 @@
/*
- * Samsung S5P/EXYNOS4 SoC series camera interface (video postprocessor) driver
+ * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver
*
- * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd.
- * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ * Copyright (C) 2010-2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
@@ -28,6 +28,7 @@
#include <media/videobuf2-dma-contig.h>
#include "fimc-core.h"
+#include "fimc-reg.h"
#include "fimc-mdevice.h"
static char *fimc_clocks[MAX_FIMC_CLOCKS] = {
@@ -39,7 +40,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "RGB565",
.fourcc = V4L2_PIX_FMT_RGB565,
.depth = { 16 },
- .color = S5P_FIMC_RGB565,
+ .color = FIMC_FMT_RGB565,
.memplanes = 1,
.colplanes = 1,
.flags = FMT_FLAGS_M2M,
@@ -47,7 +48,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "BGR666",
.fourcc = V4L2_PIX_FMT_BGR666,
.depth = { 32 },
- .color = S5P_FIMC_RGB666,
+ .color = FIMC_FMT_RGB666,
.memplanes = 1,
.colplanes = 1,
.flags = FMT_FLAGS_M2M,
@@ -55,7 +56,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "ARGB8888, 32 bpp",
.fourcc = V4L2_PIX_FMT_RGB32,
.depth = { 32 },
- .color = S5P_FIMC_RGB888,
+ .color = FIMC_FMT_RGB888,
.memplanes = 1,
.colplanes = 1,
.flags = FMT_FLAGS_M2M | FMT_HAS_ALPHA,
@@ -63,7 +64,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "ARGB1555",
.fourcc = V4L2_PIX_FMT_RGB555,
.depth = { 16 },
- .color = S5P_FIMC_RGB555,
+ .color = FIMC_FMT_RGB555,
.memplanes = 1,
.colplanes = 1,
.flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
@@ -71,7 +72,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "ARGB4444",
.fourcc = V4L2_PIX_FMT_RGB444,
.depth = { 16 },
- .color = S5P_FIMC_RGB444,
+ .color = FIMC_FMT_RGB444,
.memplanes = 1,
.colplanes = 1,
.flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA,
@@ -79,7 +80,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:2 packed, YCbYCr",
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = { 16 },
- .color = S5P_FIMC_YCBYCR422,
+ .color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
.colplanes = 1,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
@@ -88,7 +89,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:2 packed, CbYCrY",
.fourcc = V4L2_PIX_FMT_UYVY,
.depth = { 16 },
- .color = S5P_FIMC_CBYCRY422,
+ .color = FIMC_FMT_CBYCRY422,
.memplanes = 1,
.colplanes = 1,
.mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
@@ -97,7 +98,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:2 packed, CrYCbY",
.fourcc = V4L2_PIX_FMT_VYUY,
.depth = { 16 },
- .color = S5P_FIMC_CRYCBY422,
+ .color = FIMC_FMT_CRYCBY422,
.memplanes = 1,
.colplanes = 1,
.mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
@@ -106,7 +107,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:2 packed, YCrYCb",
.fourcc = V4L2_PIX_FMT_YVYU,
.depth = { 16 },
- .color = S5P_FIMC_YCRYCB422,
+ .color = FIMC_FMT_YCRYCB422,
.memplanes = 1,
.colplanes = 1,
.mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
@@ -115,7 +116,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:2 planar, Y/Cb/Cr",
.fourcc = V4L2_PIX_FMT_YUV422P,
.depth = { 12 },
- .color = S5P_FIMC_YCBYCR422,
+ .color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
.colplanes = 3,
.flags = FMT_FLAGS_M2M,
@@ -123,7 +124,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:2 planar, Y/CbCr",
.fourcc = V4L2_PIX_FMT_NV16,
.depth = { 16 },
- .color = S5P_FIMC_YCBYCR422,
+ .color = FIMC_FMT_YCBYCR422,
.memplanes = 1,
.colplanes = 2,
.flags = FMT_FLAGS_M2M,
@@ -131,7 +132,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:2 planar, Y/CrCb",
.fourcc = V4L2_PIX_FMT_NV61,
.depth = { 16 },
- .color = S5P_FIMC_YCRYCB422,
+ .color = FIMC_FMT_YCRYCB422,
.memplanes = 1,
.colplanes = 2,
.flags = FMT_FLAGS_M2M,
@@ -139,7 +140,7 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:0 planar, YCbCr",
.fourcc = V4L2_PIX_FMT_YUV420,
.depth = { 12 },
- .color = S5P_FIMC_YCBCR420,
+ .color = FIMC_FMT_YCBCR420,
.memplanes = 1,
.colplanes = 3,
.flags = FMT_FLAGS_M2M,
@@ -147,14 +148,14 @@ static struct fimc_fmt fimc_formats[] = {
.name = "YUV 4:2:0 planar, Y/CbCr",
.fourcc = V4L2_PIX_FMT_NV12,
.depth = { 12 },
- .color = S5P_FIMC_YCBCR420,
+ .color = FIMC_FMT_YCBCR420,
.memplanes = 1,
.colplanes = 2,
.flags = FMT_FLAGS_M2M,
}, {
.name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr",
.fourcc = V4L2_PIX_FMT_NV12M,
- .color = S5P_FIMC_YCBCR420,
+ .color = FIMC_FMT_YCBCR420,
.depth = { 8, 4 },
.memplanes = 2,
.colplanes = 2,
@@ -162,7 +163,7 @@ static struct fimc_fmt fimc_formats[] = {
}, {
.name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr",
.fourcc = V4L2_PIX_FMT_YUV420M,
- .color = S5P_FIMC_YCBCR420,
+ .color = FIMC_FMT_YCBCR420,
.depth = { 8, 2, 2 },
.memplanes = 3,
.colplanes = 3,
@@ -170,7 +171,7 @@ static struct fimc_fmt fimc_formats[] = {
}, {
.name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled",
.fourcc = V4L2_PIX_FMT_NV12MT,
- .color = S5P_FIMC_YCBCR420,
+ .color = FIMC_FMT_YCBCR420,
.depth = { 8, 4 },
.memplanes = 2,
.colplanes = 2,
@@ -178,7 +179,7 @@ static struct fimc_fmt fimc_formats[] = {
}, {
.name = "JPEG encoded data",
.fourcc = V4L2_PIX_FMT_JPEG,
- .color = S5P_FIMC_JPEG,
+ .color = FIMC_FMT_JPEG,
.depth = { 8 },
.memplanes = 1,
.colplanes = 1,
@@ -187,12 +188,12 @@ static struct fimc_fmt fimc_formats[] = {
},
};
-static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
+struct fimc_fmt *fimc_get_format(unsigned int index)
{
- if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- return FMT_FLAGS_M2M_IN;
- else
- return FMT_FLAGS_M2M_OUT;
+ if (index >= ARRAY_SIZE(fimc_formats))
+ return NULL;
+
+ return &fimc_formats[index];
}
int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
@@ -230,7 +231,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
int fimc_set_scaler_info(struct fimc_ctx *ctx)
{
- struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ struct fimc_variant *variant = ctx->fimc_dev->variant;
struct device *dev = &ctx->fimc_dev->pdev->dev;
struct fimc_scaler *sc = &ctx->scaler;
struct fimc_frame *s_frame = &ctx->s_frame;
@@ -293,126 +294,9 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx)
return 0;
}
-static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
-{
- struct vb2_buffer *src_vb, *dst_vb;
-
- if (!ctx || !ctx->m2m_ctx)
- return;
-
- src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
- dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
-
- if (src_vb && dst_vb) {
- v4l2_m2m_buf_done(src_vb, vb_state);
- v4l2_m2m_buf_done(dst_vb, vb_state);
- v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
- ctx->m2m_ctx);
- }
-}
-
-/* Complete the transaction which has been scheduled for execution. */
-static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
-{
- struct fimc_dev *fimc = ctx->fimc_dev;
- int ret;
-
- if (!fimc_m2m_pending(fimc))
- return 0;
-
- fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx);
-
- ret = wait_event_timeout(fimc->irq_queue,
- !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
- FIMC_SHUTDOWN_TIMEOUT);
-
- return ret == 0 ? -ETIMEDOUT : ret;
-}
-
-static int start_streaming(struct vb2_queue *q, unsigned int count)
-{
- struct fimc_ctx *ctx = q->drv_priv;
- int ret;
-
- ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
- return ret > 0 ? 0 : ret;
-}
-
-static int stop_streaming(struct vb2_queue *q)
-{
- struct fimc_ctx *ctx = q->drv_priv;
- int ret;
-
- ret = fimc_m2m_shutdown(ctx);
- if (ret == -ETIMEDOUT)
- fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
-
- pm_runtime_put(&ctx->fimc_dev->pdev->dev);
- return 0;
-}
-
-void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final)
-{
- struct fimc_vid_cap *cap = &fimc->vid_cap;
- struct fimc_vid_buffer *v_buf;
- struct timeval *tv;
- struct timespec ts;
-
- if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) {
- wake_up(&fimc->irq_queue);
- return;
- }
-
- if (!list_empty(&cap->active_buf_q) &&
- test_bit(ST_CAPT_RUN, &fimc->state) && final) {
- ktime_get_real_ts(&ts);
-
- v_buf = fimc_active_queue_pop(cap);
-
- tv = &v_buf->vb.v4l2_buf.timestamp;
- tv->tv_sec = ts.tv_sec;
- tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
- v_buf->vb.v4l2_buf.sequence = cap->frame_count++;
-
- vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE);
- }
-
- if (!list_empty(&cap->pending_buf_q)) {
-
- v_buf = fimc_pending_queue_pop(cap);
- fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index);
- v_buf->index = cap->buf_index;
-
- /* Move the buffer to the capture active queue */
- fimc_active_queue_add(cap, v_buf);
-
- dbg("next frame: %d, done frame: %d",
- fimc_hw_get_frame_index(fimc), v_buf->index);
-
- if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
- cap->buf_index = 0;
- }
-
- if (cap->active_buf_cnt == 0) {
- if (final)
- clear_bit(ST_CAPT_RUN, &fimc->state);
-
- if (++cap->buf_index >= FIMC_MAX_OUT_BUFS)
- cap->buf_index = 0;
- } else {
- set_bit(ST_CAPT_RUN, &fimc->state);
- }
-
- fimc_capture_config_update(cap->ctx);
-
- dbg("frame: %d, active_buf_cnt: %d",
- fimc_hw_get_frame_index(fimc), cap->active_buf_cnt);
-}
-
static irqreturn_t fimc_irq_handler(int irq, void *priv)
{
struct fimc_dev *fimc = priv;
- struct fimc_vid_cap *cap = &fimc->vid_cap;
struct fimc_ctx *ctx;
fimc_hw_clear_irq(fimc);
@@ -430,21 +314,16 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv)
spin_unlock(&fimc->slock);
fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE);
- spin_lock(&ctx->slock);
if (ctx->state & FIMC_CTX_SHUT) {
ctx->state &= ~FIMC_CTX_SHUT;
wake_up(&fimc->irq_queue);
}
- spin_unlock(&ctx->slock);
+ return IRQ_HANDLED;
}
- return IRQ_HANDLED;
} else if (test_bit(ST_CAPT_PEND, &fimc->state)) {
- fimc_capture_irq_handler(fimc,
- !test_bit(ST_CAPT_JPEG, &fimc->state));
- if (cap->active_buf_cnt == 1) {
- fimc_deactivate_capture(fimc);
- clear_bit(ST_CAPT_STREAM, &fimc->state);
- }
+ int last_buf = test_bit(ST_CAPT_JPEG, &fimc->state) &&
+ fimc->vid_cap.reqbufs_count == 1;
+ fimc_capture_irq_handler(fimc, !last_buf);
}
out:
spin_unlock(&fimc->slock);
@@ -482,7 +361,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
case 3:
paddr->cb = (u32)(paddr->y + pix_size);
/* decompose Y into Y/Cb/Cr */
- if (S5P_FIMC_YCBCR420 == frame->fmt->color)
+ if (FIMC_FMT_YCBCR420 == frame->fmt->color)
paddr->cr = (u32)(paddr->cb
+ (pix_size >> 2));
else /* 422 */
@@ -510,40 +389,40 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
void fimc_set_yuv_order(struct fimc_ctx *ctx)
{
/* The one only mode supported in SoC. */
- ctx->in_order_2p = S5P_FIMC_LSB_CRCB;
- ctx->out_order_2p = S5P_FIMC_LSB_CRCB;
+ ctx->in_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB;
+ ctx->out_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB;
/* Set order for 1 plane input formats. */
switch (ctx->s_frame.fmt->color) {
- case S5P_FIMC_YCRYCB422:
- ctx->in_order_1p = S5P_MSCTRL_ORDER422_CBYCRY;
+ case FIMC_FMT_YCRYCB422:
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY;
break;
- case S5P_FIMC_CBYCRY422:
- ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCRYCB;
+ case FIMC_FMT_CBYCRY422:
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB;
break;
- case S5P_FIMC_CRYCBY422:
- ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCBYCR;
+ case FIMC_FMT_CRYCBY422:
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR;
break;
- case S5P_FIMC_YCBYCR422:
+ case FIMC_FMT_YCBYCR422:
default:
- ctx->in_order_1p = S5P_MSCTRL_ORDER422_CRYCBY;
+ ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY;
break;
}
dbg("ctx->in_order_1p= %d", ctx->in_order_1p);
switch (ctx->d_frame.fmt->color) {
- case S5P_FIMC_YCRYCB422:
- ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CBYCRY;
+ case FIMC_FMT_YCRYCB422:
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY;
break;
- case S5P_FIMC_CBYCRY422:
- ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCRYCB;
+ case FIMC_FMT_CBYCRY422:
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB;
break;
- case S5P_FIMC_CRYCBY422:
- ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCBYCR;
+ case FIMC_FMT_CRYCBY422:
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR;
break;
- case S5P_FIMC_YCBYCR422:
+ case FIMC_FMT_YCBYCR422:
default:
- ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CRYCBY;
+ ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY;
break;
}
dbg("ctx->out_order_1p= %d", ctx->out_order_1p);
@@ -551,7 +430,7 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx)
void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
{
- struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ struct fimc_variant *variant = ctx->fimc_dev->variant;
u32 i, depth = 0;
for (i = 0; i < f->fmt->colplanes; i++)
@@ -574,7 +453,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
f->dma_offset.cb_h >>= 1;
f->dma_offset.cr_h >>= 1;
}
- if (f->fmt->color == S5P_FIMC_YCBCR420) {
+ if (f->fmt->color == FIMC_FMT_YCBCR420) {
f->dma_offset.cb_v >>= 1;
f->dma_offset.cr_v >>= 1;
}
@@ -584,203 +463,58 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f)
f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v);
}
-/**
- * fimc_prepare_config - check dimensions, operation and color mode
- * and pre-calculate offset and the scaling coefficients.
- *
- * @ctx: hardware context information
- * @flags: flags indicating which parameters to check/update
- *
- * Return: 0 if dimensions are valid or non zero otherwise.
- */
-int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags)
-{
- struct fimc_frame *s_frame, *d_frame;
- struct vb2_buffer *vb = NULL;
- int ret = 0;
-
- s_frame = &ctx->s_frame;
- d_frame = &ctx->d_frame;
-
- if (flags & FIMC_PARAMS) {
- /* Prepare the DMA offset ratios for scaler. */
- fimc_prepare_dma_offset(ctx, &ctx->s_frame);
- fimc_prepare_dma_offset(ctx, &ctx->d_frame);
-
- if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) ||
- s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) {
- err("out of scaler range");
- return -EINVAL;
- }
- fimc_set_yuv_order(ctx);
- }
-
- if (flags & FIMC_SRC_ADDR) {
- vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr);
- if (ret)
- return ret;
- }
-
- if (flags & FIMC_DST_ADDR) {
- vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr);
- }
-
- return ret;
-}
-
-static void fimc_dma_run(void *priv)
-{
- struct fimc_ctx *ctx = priv;
- struct fimc_dev *fimc;
- unsigned long flags;
- u32 ret;
-
- if (WARN(!ctx, "null hardware context\n"))
- return;
-
- fimc = ctx->fimc_dev;
- spin_lock_irqsave(&fimc->slock, flags);
- set_bit(ST_M2M_PEND, &fimc->state);
-
- spin_lock(&ctx->slock);
- ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR);
- ret = fimc_prepare_config(ctx, ctx->state);
- if (ret)
- goto dma_unlock;
-
- /* Reconfigure hardware if the context has changed. */
- if (fimc->m2m.ctx != ctx) {
- ctx->state |= FIMC_PARAMS;
- fimc->m2m.ctx = ctx;
- }
- fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr);
-
- if (ctx->state & FIMC_PARAMS) {
- fimc_hw_set_input_path(ctx);
- fimc_hw_set_in_dma(ctx);
- ret = fimc_set_scaler_info(ctx);
- if (ret) {
- spin_unlock(&fimc->slock);
- goto dma_unlock;
- }
- fimc_hw_set_prescaler(ctx);
- fimc_hw_set_mainscaler(ctx);
- fimc_hw_set_target_format(ctx);
- fimc_hw_set_rotation(ctx);
- fimc_hw_set_effect(ctx, false);
- }
-
- fimc_hw_set_output_path(ctx);
- if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS))
- fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1);
-
- if (ctx->state & FIMC_PARAMS) {
- fimc_hw_set_out_dma(ctx);
- if (fimc->variant->has_alpha)
- fimc_hw_set_rgb_alpha(ctx);
- }
-
- fimc_activate_capture(ctx);
-
- ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
- FIMC_SRC_FMT | FIMC_DST_FMT);
- fimc_hw_activate_input_dma(fimc, true);
-dma_unlock:
- spin_unlock(&ctx->slock);
- spin_unlock_irqrestore(&fimc->slock, flags);
-}
-
-static void fimc_job_abort(void *priv)
+int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx)
{
- fimc_m2m_shutdown(priv);
-}
-
-static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
- unsigned int *num_buffers, unsigned int *num_planes,
- unsigned int sizes[], void *allocators[])
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- struct fimc_frame *f;
- int i;
+ struct fimc_effect *effect = &ctx->effect;
- f = ctx_get_frame(ctx, vq->type);
- if (IS_ERR(f))
- return PTR_ERR(f);
- /*
- * Return number of non-contigous planes (plane buffers)
- * depending on the configured color format.
- */
- if (!f->fmt)
+ switch (colorfx) {
+ case V4L2_COLORFX_NONE:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_BYPASS;
+ break;
+ case V4L2_COLORFX_BW:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY;
+ effect->pat_cb = 128;
+ effect->pat_cr = 128;
+ break;
+ case V4L2_COLORFX_SEPIA:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY;
+ effect->pat_cb = 115;
+ effect->pat_cr = 145;
+ break;
+ case V4L2_COLORFX_NEGATIVE:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_NEGATIVE;
+ break;
+ case V4L2_COLORFX_EMBOSS:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_EMBOSSING;
+ break;
+ case V4L2_COLORFX_ART_FREEZE:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_ARTFREEZE;
+ break;
+ case V4L2_COLORFX_SILHOUETTE:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_SILHOUETTE;
+ break;
+ case V4L2_COLORFX_SET_CBCR:
+ effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY;
+ effect->pat_cb = ctx->ctrls.colorfx_cbcr->val >> 8;
+ effect->pat_cr = ctx->ctrls.colorfx_cbcr->val & 0xff;
+ break;
+ default:
return -EINVAL;
-
- *num_planes = f->fmt->memplanes;
- for (i = 0; i < f->fmt->memplanes; i++) {
- sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8;
- allocators[i] = ctx->fimc_dev->alloc_ctx;
}
- return 0;
-}
-
-static int fimc_buf_prepare(struct vb2_buffer *vb)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- struct fimc_frame *frame;
- int i;
-
- frame = ctx_get_frame(ctx, vb->vb2_queue->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- for (i = 0; i < frame->fmt->memplanes; i++)
- vb2_set_plane_payload(vb, i, frame->payload[i]);
return 0;
}
-static void fimc_buf_queue(struct vb2_buffer *vb)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
-
- dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
-
- if (ctx->m2m_ctx)
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
-}
-
-static void fimc_lock(struct vb2_queue *vq)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- mutex_lock(&ctx->fimc_dev->lock);
-}
-
-static void fimc_unlock(struct vb2_queue *vq)
-{
- struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
- mutex_unlock(&ctx->fimc_dev->lock);
-}
-
-static struct vb2_ops fimc_qops = {
- .queue_setup = fimc_queue_setup,
- .buf_prepare = fimc_buf_prepare,
- .buf_queue = fimc_buf_queue,
- .wait_prepare = fimc_unlock,
- .wait_finish = fimc_lock,
- .stop_streaming = stop_streaming,
- .start_streaming = start_streaming,
-};
-
/*
* V4L2 controls handling
*/
#define ctrl_to_ctx(__ctrl) \
- container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler)
+ container_of((__ctrl)->handler, struct fimc_ctx, ctrls.handler)
static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl)
{
struct fimc_dev *fimc = ctx->fimc_dev;
- struct samsung_fimc_variant *variant = fimc->variant;
+ struct fimc_variant *variant = fimc->variant;
unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT;
int ret = 0;
@@ -815,7 +549,14 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl)
case V4L2_CID_ALPHA_COMPONENT:
ctx->d_frame.alpha = ctrl->val;
break;
+
+ case V4L2_CID_COLORFX:
+ ret = fimc_set_color_effect(ctx, ctrl->val);
+ if (ret)
+ return ret;
+ break;
}
+
ctx->state |= FIMC_PARAMS;
set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
return 0;
@@ -827,9 +568,9 @@ static int fimc_s_ctrl(struct v4l2_ctrl *ctrl)
unsigned long flags;
int ret;
- spin_lock_irqsave(&ctx->slock, flags);
+ spin_lock_irqsave(&ctx->fimc_dev->slock, flags);
ret = __fimc_s_ctrl(ctx, ctrl);
- spin_unlock_irqrestore(&ctx->slock, flags);
+ spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);
return ret;
}
@@ -840,71 +581,93 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = {
int fimc_ctrls_create(struct fimc_ctx *ctx)
{
- struct samsung_fimc_variant *variant = ctx->fimc_dev->variant;
+ struct fimc_variant *variant = ctx->fimc_dev->variant;
unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt);
+ struct fimc_ctrls *ctrls = &ctx->ctrls;
+ struct v4l2_ctrl_handler *handler = &ctrls->handler;
- if (ctx->ctrls_rdy)
+ if (ctx->ctrls.ready)
return 0;
- v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
- ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ v4l2_ctrl_handler_init(handler, 6);
+
+ ctrls->rotate = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,
V4L2_CID_ROTATE, 0, 270, 90, 0);
- ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ ctrls->hflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
- ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops,
+ ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
+
if (variant->has_alpha)
- ctx->ctrl_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler,
- &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
- 0, max_alpha, 1, 0);
+ ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,
+ V4L2_CID_ALPHA_COMPONENT,
+ 0, max_alpha, 1, 0);
else
- ctx->ctrl_alpha = NULL;
+ ctrls->alpha = NULL;
- ctx->ctrls_rdy = ctx->ctrl_handler.error == 0;
+ ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, &fimc_ctrl_ops,
+ V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR,
+ ~0x983f, V4L2_COLORFX_NONE);
- return ctx->ctrl_handler.error;
+ ctrls->colorfx_cbcr = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops,
+ V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0);
+
+ ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS;
+
+ if (!handler->error) {
+ v4l2_ctrl_cluster(3, &ctrls->colorfx);
+ ctrls->ready = true;
+ }
+
+ return handler->error;
}
void fimc_ctrls_delete(struct fimc_ctx *ctx)
{
- if (ctx->ctrls_rdy) {
- v4l2_ctrl_handler_free(&ctx->ctrl_handler);
- ctx->ctrls_rdy = false;
- ctx->ctrl_alpha = NULL;
+ struct fimc_ctrls *ctrls = &ctx->ctrls;
+
+ if (ctrls->ready) {
+ v4l2_ctrl_handler_free(&ctrls->handler);
+ ctrls->ready = false;
+ ctrls->alpha = NULL;
}
}
void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active)
{
unsigned int has_alpha = ctx->d_frame.fmt->flags & FMT_HAS_ALPHA;
+ struct fimc_ctrls *ctrls = &ctx->ctrls;
- if (!ctx->ctrls_rdy)
+ if (!ctrls->ready)
return;
- mutex_lock(&ctx->ctrl_handler.lock);
- v4l2_ctrl_activate(ctx->ctrl_rotate, active);
- v4l2_ctrl_activate(ctx->ctrl_hflip, active);
- v4l2_ctrl_activate(ctx->ctrl_vflip, active);
- if (ctx->ctrl_alpha)
- v4l2_ctrl_activate(ctx->ctrl_alpha, active && has_alpha);
+ mutex_lock(&ctrls->handler.lock);
+ v4l2_ctrl_activate(ctrls->rotate, active);
+ v4l2_ctrl_activate(ctrls->hflip, active);
+ v4l2_ctrl_activate(ctrls->vflip, active);
+ v4l2_ctrl_activate(ctrls->colorfx, active);
+ if (ctrls->alpha)
+ v4l2_ctrl_activate(ctrls->alpha, active && has_alpha);
if (active) {
- ctx->rotation = ctx->ctrl_rotate->val;
- ctx->hflip = ctx->ctrl_hflip->val;
- ctx->vflip = ctx->ctrl_vflip->val;
+ fimc_set_color_effect(ctx, ctrls->colorfx->cur.val);
+ ctx->rotation = ctrls->rotate->val;
+ ctx->hflip = ctrls->hflip->val;
+ ctx->vflip = ctrls->vflip->val;
} else {
+ ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS;
ctx->rotation = 0;
ctx->hflip = 0;
ctx->vflip = 0;
}
- mutex_unlock(&ctx->ctrl_handler.lock);
+ mutex_unlock(&ctrls->handler.lock);
}
/* Update maximum value of the alpha color control */
void fimc_alpha_ctrl_update(struct fimc_ctx *ctx)
{
struct fimc_dev *fimc = ctx->fimc_dev;
- struct v4l2_ctrl *ctrl = ctx->ctrl_alpha;
+ struct v4l2_ctrl *ctrl = ctx->ctrls.alpha;
if (ctrl == NULL || !fimc->variant->has_alpha)
return;
@@ -918,39 +681,6 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx)
v4l2_ctrl_unlock(ctrl);
}
-/*
- * V4L2 ioctl handlers
- */
-static int fimc_m2m_querycap(struct file *file, void *fh,
- struct v4l2_capability *cap)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_dev *fimc = ctx->fimc_dev;
-
- strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
- strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
- cap->bus_info[0] = 0;
- cap->capabilities = V4L2_CAP_STREAMING |
- V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
-
- return 0;
-}
-
-static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct fimc_fmt *fmt;
-
- fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
- f->index);
- if (!fmt)
- return -EINVAL;
-
- strncpy(f->description, fmt->name, sizeof(f->description) - 1);
- f->pixelformat = fmt->fourcc;
- return 0;
-}
-
int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
@@ -1029,18 +759,6 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
}
}
-static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
-
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- return fimc_fill_format(frame, f);
-}
-
/**
* fimc_find_format - lookup fimc color format by fourcc or media bus format
* @pixelformat: fourcc to match, ignored if null
@@ -1073,535 +791,10 @@ struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code,
return def_fmt;
}
-static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
-{
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct samsung_fimc_variant *variant = fimc->variant;
- struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
- struct fimc_fmt *fmt;
- u32 max_w, mod_x, mod_y;
-
- if (!IS_M2M(f->type))
- return -EINVAL;
-
- dbg("w: %d, h: %d", pix->width, pix->height);
-
- fmt = fimc_find_format(&pix->pixelformat, NULL,
- get_m2m_fmt_flags(f->type), 0);
- if (WARN(fmt == NULL, "Pixel format lookup failed"))
- return -EINVAL;
-
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = V4L2_FIELD_NONE;
- else if (pix->field != V4L2_FIELD_NONE)
- return -EINVAL;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- max_w = variant->pix_limit->scaler_dis_w;
- mod_x = ffs(variant->min_inp_pixsize) - 1;
- } else {
- max_w = variant->pix_limit->out_rot_dis_w;
- mod_x = ffs(variant->min_out_pixsize) - 1;
- }
-
- if (tiled_fmt(fmt)) {
- mod_x = 6; /* 64 x 32 pixels tile */
- mod_y = 5;
- } else {
- if (variant->min_vsize_align == 1)
- mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
- else
- mod_y = ffs(variant->min_vsize_align) - 1;
- }
-
- v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
- &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
-
- fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
- return 0;
-}
-
-static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return fimc_try_fmt_mplane(ctx, f);
-}
-
-static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
- struct v4l2_format *f)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct vb2_queue *vq;
- struct fimc_frame *frame;
- struct v4l2_pix_format_mplane *pix;
- int i, ret = 0;
-
- ret = fimc_try_fmt_mplane(ctx, f);
- if (ret)
- return ret;
-
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
-
- if (vb2_is_busy(vq)) {
- v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type);
- return -EBUSY;
- }
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- frame = &ctx->s_frame;
- else
- frame = &ctx->d_frame;
-
- pix = &f->fmt.pix_mp;
- frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
- get_m2m_fmt_flags(f->type), 0);
- if (!frame->fmt)
- return -EINVAL;
-
- /* Update RGB Alpha control state and value range */
- fimc_alpha_ctrl_update(ctx);
-
- for (i = 0; i < frame->fmt->colplanes; i++) {
- frame->payload[i] =
- (pix->width * pix->height * frame->fmt->depth[i]) / 8;
- }
-
- fimc_fill_frame(frame, f);
-
- ctx->scaler.enabled = 1;
-
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
- else
- fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx);
-
- dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
-
- return 0;
-}
-
-static int fimc_m2m_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *reqbufs)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
-}
-
-static int fimc_m2m_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_qbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_dqbuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
-}
-
-static int fimc_m2m_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- /* The source and target color format need to be set */
- if (V4L2_TYPE_IS_OUTPUT(type)) {
- if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx))
- return -EINVAL;
- } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) {
- return -EINVAL;
- }
-
- return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
-}
-
-static int fimc_m2m_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
-
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
-}
-
-static int fimc_m2m_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cr)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_frame *frame;
-
- frame = ctx_get_frame(ctx, cr->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- cr->bounds.left = 0;
- cr->bounds.top = 0;
- cr->bounds.width = frame->o_width;
- cr->bounds.height = frame->o_height;
- cr->defrect = cr->bounds;
-
- return 0;
-}
-
-static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_frame *frame;
-
- frame = ctx_get_frame(ctx, cr->type);
- if (IS_ERR(frame))
- return PTR_ERR(frame);
-
- cr->c.left = frame->offs_h;
- cr->c.top = frame->offs_v;
- cr->c.width = frame->width;
- cr->c.height = frame->height;
-
- return 0;
-}
-
-static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
-{
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_frame *f;
- u32 min_size, halign, depth = 0;
- int i;
-
- if (cr->c.top < 0 || cr->c.left < 0) {
- v4l2_err(fimc->m2m.vfd,
- "doesn't support negative values for top & left\n");
- return -EINVAL;
- }
- if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- f = &ctx->d_frame;
- else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
- f = &ctx->s_frame;
- else
- return -EINVAL;
-
- min_size = (f == &ctx->s_frame) ?
- fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
-
- /* Get pixel alignment constraints. */
- if (fimc->variant->min_vsize_align == 1)
- halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
- else
- halign = ffs(fimc->variant->min_vsize_align) - 1;
-
- for (i = 0; i < f->fmt->colplanes; i++)
- depth += f->fmt->depth[i];
-
- v4l_bound_align_image(&cr->c.width, min_size, f->o_width,
- ffs(min_size) - 1,
- &cr->c.height, min_size, f->o_height,
- halign, 64/(ALIGN(depth, 8)));
-
- /* adjust left/top if cropping rectangle is out of bounds */
- if (cr->c.left + cr->c.width > f->o_width)
- cr->c.left = f->o_width - cr->c.width;
- if (cr->c.top + cr->c.height > f->o_height)
- cr->c.top = f->o_height - cr->c.height;
-
- cr->c.left = round_down(cr->c.left, min_size);
- cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align);
-
- dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
- cr->c.left, cr->c.top, cr->c.width, cr->c.height,
- f->f_width, f->f_height);
-
- return 0;
-}
-
-static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
-{
- struct fimc_ctx *ctx = fh_to_ctx(fh);
- struct fimc_dev *fimc = ctx->fimc_dev;
- struct fimc_frame *f;
- int ret;
-
- ret = fimc_m2m_try_crop(ctx, cr);
- if (ret)
- return ret;
-
- f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
- &ctx->s_frame : &ctx->d_frame;
-
- /* Check to see if scaling ratio is within supported range */
- if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
- if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- ret = fimc_check_scaler_ratio(ctx, cr->c.width,
- cr->c.height, ctx->d_frame.width,
- ctx->d_frame.height, ctx->rotation);
- } else {
- ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
- ctx->s_frame.height, cr->c.width,
- cr->c.height, ctx->rotation);
- }
- if (ret) {
- v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
- return -EINVAL;
- }
- }
-
- f->offs_h = cr->c.left;
- f->offs_v = cr->c.top;
- f->width = cr->c.width;
- f->height = cr->c.height;
-
- fimc_ctx_state_lock_set(FIMC_PARAMS, ctx);
-
- return 0;
-}
-
-static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
- .vidioc_querycap = fimc_m2m_querycap,
-
- .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
- .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
-
- .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
- .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
-
- .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
- .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
-
- .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
- .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
-
- .vidioc_reqbufs = fimc_m2m_reqbufs,
- .vidioc_querybuf = fimc_m2m_querybuf,
-
- .vidioc_qbuf = fimc_m2m_qbuf,
- .vidioc_dqbuf = fimc_m2m_dqbuf,
-
- .vidioc_streamon = fimc_m2m_streamon,
- .vidioc_streamoff = fimc_m2m_streamoff,
-
- .vidioc_g_crop = fimc_m2m_g_crop,
- .vidioc_s_crop = fimc_m2m_s_crop,
- .vidioc_cropcap = fimc_m2m_cropcap
-
-};
-
-static int queue_init(void *priv, struct vb2_queue *src_vq,
- struct vb2_queue *dst_vq)
-{
- struct fimc_ctx *ctx = priv;
- int ret;
-
- memset(src_vq, 0, sizeof(*src_vq));
- src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
- src_vq->drv_priv = ctx;
- src_vq->ops = &fimc_qops;
- src_vq->mem_ops = &vb2_dma_contig_memops;
- src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
-
- ret = vb2_queue_init(src_vq);
- if (ret)
- return ret;
-
- memset(dst_vq, 0, sizeof(*dst_vq));
- dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
- dst_vq->drv_priv = ctx;
- dst_vq->ops = &fimc_qops;
- dst_vq->mem_ops = &vb2_dma_contig_memops;
- dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
-
- return vb2_queue_init(dst_vq);
-}
-
-static int fimc_m2m_open(struct file *file)
-{
- struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_ctx *ctx;
- int ret;
-
- dbg("pid: %d, state: 0x%lx, refcnt: %d",
- task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
-
- /*
- * Return if the corresponding video capture node
- * is already opened.
- */
- if (fimc->vid_cap.refcnt > 0)
- return -EBUSY;
-
- ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
- v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
- ctx->fimc_dev = fimc;
-
- /* Default color format */
- ctx->s_frame.fmt = &fimc_formats[0];
- ctx->d_frame.fmt = &fimc_formats[0];
-
- ret = fimc_ctrls_create(ctx);
- if (ret)
- goto error_fh;
-
- /* Use separate control handler per file handle */
- ctx->fh.ctrl_handler = &ctx->ctrl_handler;
- file->private_data = &ctx->fh;
- v4l2_fh_add(&ctx->fh);
-
- /* Setup the device context for memory-to-memory mode */
- ctx->state = FIMC_CTX_M2M;
- ctx->flags = 0;
- ctx->in_path = FIMC_DMA;
- ctx->out_path = FIMC_DMA;
- spin_lock_init(&ctx->slock);
-
- ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
- if (IS_ERR(ctx->m2m_ctx)) {
- ret = PTR_ERR(ctx->m2m_ctx);
- goto error_c;
- }
-
- if (fimc->m2m.refcnt++ == 0)
- set_bit(ST_M2M_RUN, &fimc->state);
- return 0;
-
-error_c:
- fimc_ctrls_delete(ctx);
-error_fh:
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
- kfree(ctx);
- return ret;
-}
-
-static int fimc_m2m_release(struct file *file)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
- struct fimc_dev *fimc = ctx->fimc_dev;
-
- dbg("pid: %d, state: 0x%lx, refcnt= %d",
- task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
-
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
- fimc_ctrls_delete(ctx);
- v4l2_fh_del(&ctx->fh);
- v4l2_fh_exit(&ctx->fh);
-
- if (--fimc->m2m.refcnt <= 0)
- clear_bit(ST_M2M_RUN, &fimc->state);
- kfree(ctx);
- return 0;
-}
-
-static unsigned int fimc_m2m_poll(struct file *file,
- struct poll_table_struct *wait)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
-
- return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
-}
-
-
-static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
-
- return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
-}
-
-static const struct v4l2_file_operations fimc_m2m_fops = {
- .owner = THIS_MODULE,
- .open = fimc_m2m_open,
- .release = fimc_m2m_release,
- .poll = fimc_m2m_poll,
- .unlocked_ioctl = video_ioctl2,
- .mmap = fimc_m2m_mmap,
-};
-
-static struct v4l2_m2m_ops m2m_ops = {
- .device_run = fimc_dma_run,
- .job_abort = fimc_job_abort,
-};
-
-int fimc_register_m2m_device(struct fimc_dev *fimc,
- struct v4l2_device *v4l2_dev)
-{
- struct video_device *vfd;
- struct platform_device *pdev;
- int ret = 0;
-
- if (!fimc)
- return -ENODEV;
-
- pdev = fimc->pdev;
- fimc->v4l2_dev = v4l2_dev;
-
- vfd = video_device_alloc();
- if (!vfd) {
- v4l2_err(v4l2_dev, "Failed to allocate video device\n");
- return -ENOMEM;
- }
-
- vfd->fops = &fimc_m2m_fops;
- vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
- vfd->v4l2_dev = v4l2_dev;
- vfd->minor = -1;
- vfd->release = video_device_release;
- vfd->lock = &fimc->lock;
-
- snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev));
- video_set_drvdata(vfd, fimc);
-
- fimc->m2m.vfd = vfd;
- fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
- if (IS_ERR(fimc->m2m.m2m_dev)) {
- v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
- ret = PTR_ERR(fimc->m2m.m2m_dev);
- goto err_init;
- }
-
- ret = media_entity_init(&vfd->entity, 0, NULL, 0);
- if (!ret)
- return 0;
-
- v4l2_m2m_release(fimc->m2m.m2m_dev);
-err_init:
- video_device_release(fimc->m2m.vfd);
- return ret;
-}
-
-void fimc_unregister_m2m_device(struct fimc_dev *fimc)
-{
- if (!fimc)
- return;
-
- if (fimc->m2m.m2m_dev)
- v4l2_m2m_release(fimc->m2m.m2m_dev);
- if (fimc->m2m.vfd) {
- media_entity_cleanup(&fimc->m2m.vfd->entity);
- /* Can also be called if video device wasn't registered */
- video_unregister_device(fimc->m2m.vfd);
- }
-}
-
static void fimc_clk_put(struct fimc_dev *fimc)
{
int i;
- for (i = 0; i < fimc->num_clocks; i++) {
+ for (i = 0; i < MAX_FIMC_CLOCKS; i++) {
if (IS_ERR_OR_NULL(fimc->clock[i]))
continue;
clk_unprepare(fimc->clock[i]);
@@ -1614,7 +807,7 @@ static int fimc_clk_get(struct fimc_dev *fimc)
{
int i, ret;
- for (i = 0; i < fimc->num_clocks; i++) {
+ for (i = 0; i < MAX_FIMC_CLOCKS; i++) {
fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
if (IS_ERR(fimc->clock[i]))
goto err;
@@ -1672,15 +865,12 @@ static int fimc_m2m_resume(struct fimc_dev *fimc)
static int fimc_probe(struct platform_device *pdev)
{
+ struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev);
+ struct s5p_platform_fimc *pdata;
struct fimc_dev *fimc;
struct resource *res;
- struct samsung_fimc_driverdata *drv_data;
- struct s5p_platform_fimc *pdata;
int ret = 0;
- drv_data = (struct samsung_fimc_driverdata *)
- platform_get_device_id(pdev)->driver_data;
-
if (pdev->id >= drv_data->num_entities) {
dev_err(&pdev->dev, "Invalid platform device id: %d\n",
pdev->id);
@@ -1714,28 +904,29 @@ static int fimc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Failed to get IRQ resource\n");
return -ENXIO;
}
- fimc->irq = res->start;
- fimc->num_clocks = MAX_FIMC_CLOCKS;
ret = fimc_clk_get(fimc);
if (ret)
return ret;
clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
clk_enable(fimc->clock[CLK_BUS]);
- platform_set_drvdata(pdev, fimc);
-
- ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler,
- 0, pdev->name, fimc);
+ ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler,
+ 0, dev_name(&pdev->dev), fimc);
if (ret) {
dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
goto err_clk;
}
+ ret = fimc_initialize_capture_subdev(fimc);
+ if (ret)
+ goto err_clk;
+
+ platform_set_drvdata(pdev, fimc);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0)
- goto err_clk;
+ goto err_sd;
/* Initialize contiguous memory allocator */
fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(fimc->alloc_ctx)) {
@@ -1747,9 +938,10 @@ static int fimc_probe(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
return 0;
-
err_pm:
pm_runtime_put(&pdev->dev);
+err_sd:
+ fimc_unregister_capture_subdev(fimc);
err_clk:
fimc_clk_put(fimc);
return ret;
@@ -1834,6 +1026,7 @@ static int __devexit fimc_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
+ fimc_unregister_capture_subdev(fimc);
vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
clk_disable(fimc->clock[CLK_BUS]);
@@ -1879,7 +1072,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = {
},
};
-static struct samsung_fimc_variant fimc0_variant_s5p = {
+static struct fimc_variant fimc0_variant_s5p = {
.has_inp_rot = 1,
.has_out_rot = 1,
.has_cam_if = 1,
@@ -1891,17 +1084,17 @@ static struct samsung_fimc_variant fimc0_variant_s5p = {
.pix_limit = &s5p_pix_limit[0],
};
-static struct samsung_fimc_variant fimc2_variant_s5p = {
+static struct fimc_variant fimc2_variant_s5p = {
.has_cam_if = 1,
.min_inp_pixsize = 16,
.min_out_pixsize = 16,
.hor_offs_align = 8,
.min_vsize_align = 16,
.out_buf_count = 4,
- .pix_limit = &s5p_pix_limit[1],
+ .pix_limit = &s5p_pix_limit[1],
};
-static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
+static struct fimc_variant fimc0_variant_s5pv210 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
@@ -1914,7 +1107,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv210 = {
.pix_limit = &s5p_pix_limit[1],
};
-static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
+static struct fimc_variant fimc1_variant_s5pv210 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
@@ -1928,7 +1121,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = {
.pix_limit = &s5p_pix_limit[2],
};
-static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
+static struct fimc_variant fimc2_variant_s5pv210 = {
.has_cam_if = 1,
.pix_hoff = 1,
.min_inp_pixsize = 16,
@@ -1939,7 +1132,7 @@ static struct samsung_fimc_variant fimc2_variant_s5pv210 = {
.pix_limit = &s5p_pix_limit[2],
};
-static struct samsung_fimc_variant fimc0_variant_exynos4 = {
+static struct fimc_variant fimc0_variant_exynos4 = {
.pix_hoff = 1,
.has_inp_rot = 1,
.has_out_rot = 1,
@@ -1955,7 +1148,7 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = {
.pix_limit = &s5p_pix_limit[1],
};
-static struct samsung_fimc_variant fimc3_variant_exynos4 = {
+static struct fimc_variant fimc3_variant_exynos4 = {
.pix_hoff = 1,
.has_cam_if = 1,
.has_cistatus2 = 1,
@@ -1970,7 +1163,7 @@ static struct samsung_fimc_variant fimc3_variant_exynos4 = {
};
/* S5PC100 */
-static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
+static struct fimc_drvdata fimc_drvdata_s5p = {
.variant = {
[0] = &fimc0_variant_s5p,
[1] = &fimc0_variant_s5p,
@@ -1981,7 +1174,7 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5p = {
};
/* S5PV210, S5PC110 */
-static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
+static struct fimc_drvdata fimc_drvdata_s5pv210 = {
.variant = {
[0] = &fimc0_variant_s5pv210,
[1] = &fimc1_variant_s5pv210,
@@ -1991,8 +1184,8 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = {
.lclk_frequency = 166000000UL,
};
-/* S5PV310, S5PC210 */
-static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = {
+/* EXYNOS4210, S5PV310, S5PC210 */
+static struct fimc_drvdata fimc_drvdata_exynos4 = {
.variant = {
[0] = &fimc0_variant_exynos4,
[1] = &fimc0_variant_exynos4,
@@ -2036,7 +1229,7 @@ static struct platform_driver fimc_driver = {
int __init fimc_register_driver(void)
{
- return platform_driver_probe(&fimc_driver, fimc_probe);
+ return platform_driver_register(&fimc_driver);
}
void __exit fimc_unregister_driver(void)
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 84fd83550bd..95b27ae5cf2 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/videodev2.h>
#include <linux/io.h>
+#include <asm/sizes.h>
#include <media/media-entity.h>
#include <media/videobuf2-core.h>
@@ -26,8 +27,6 @@
#include <media/v4l2-mediabus.h>
#include <media/s5p_fimc.h>
-#include "regs-fimc.h"
-
#define err(fmt, args...) \
printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args)
@@ -78,26 +77,31 @@ enum fimc_dev_flags {
#define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state)
enum fimc_datapath {
- FIMC_CAMERA,
- FIMC_DMA,
- FIMC_LCDFIFO,
- FIMC_WRITEBACK
+ FIMC_IO_NONE,
+ FIMC_IO_CAMERA,
+ FIMC_IO_DMA,
+ FIMC_IO_LCDFIFO,
+ FIMC_IO_WRITEBACK,
+ FIMC_IO_ISP,
};
enum fimc_color_fmt {
- S5P_FIMC_RGB444 = 0x10,
- S5P_FIMC_RGB555,
- S5P_FIMC_RGB565,
- S5P_FIMC_RGB666,
- S5P_FIMC_RGB888,
- S5P_FIMC_RGB30_LOCAL,
- S5P_FIMC_YCBCR420 = 0x20,
- S5P_FIMC_YCBYCR422,
- S5P_FIMC_YCRYCB422,
- S5P_FIMC_CBYCRY422,
- S5P_FIMC_CRYCBY422,
- S5P_FIMC_YCBCR444_LOCAL,
- S5P_FIMC_JPEG = 0x40,
+ FIMC_FMT_RGB444 = 0x10,
+ FIMC_FMT_RGB555,
+ FIMC_FMT_RGB565,
+ FIMC_FMT_RGB666,
+ FIMC_FMT_RGB888,
+ FIMC_FMT_RGB30_LOCAL,
+ FIMC_FMT_YCBCR420 = 0x20,
+ FIMC_FMT_YCBYCR422,
+ FIMC_FMT_YCRYCB422,
+ FIMC_FMT_CBYCRY422,
+ FIMC_FMT_CRYCBY422,
+ FIMC_FMT_YCBCR444_LOCAL,
+ FIMC_FMT_JPEG = 0x40,
+ FIMC_FMT_RAW8 = 0x80,
+ FIMC_FMT_RAW10,
+ FIMC_FMT_RAW12,
};
#define fimc_fmt_is_rgb(x) (!!((x) & 0x10))
@@ -106,24 +110,11 @@ enum fimc_color_fmt {
#define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \
__strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
-/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */
-#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB
-
-/* The embedded image effect selection */
-#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS
-#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY
-#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE
-#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE
-#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING
-#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE
-
/* The hardware context state. */
#define FIMC_PARAMS (1 << 0)
-#define FIMC_SRC_ADDR (1 << 1)
-#define FIMC_DST_ADDR (1 << 2)
#define FIMC_SRC_FMT (1 << 3)
#define FIMC_DST_FMT (1 << 4)
-#define FIMC_DST_CROP (1 << 5)
+#define FIMC_COMPOSE (1 << 5)
#define FIMC_CTX_M2M (1 << 16)
#define FIMC_CTX_CAP (1 << 17)
#define FIMC_CTX_SHUT (1 << 18)
@@ -333,7 +324,7 @@ struct fimc_vid_cap {
struct fimc_ctx *ctx;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device *vfd;
- struct v4l2_subdev *subdev;
+ struct v4l2_subdev subdev;
struct media_pad vd_pad;
struct v4l2_mbus_framefmt mf;
struct media_pad sd_pads[FIMC_SD_PADS_NUM];
@@ -370,8 +361,7 @@ struct fimc_pix_limit {
};
/**
- * struct samsung_fimc_variant - camera interface variant information
- *
+ * struct fimc_variant - FIMC device variant information
* @pix_hoff: indicate whether horizontal offset is in pixels or in bytes
* @has_inp_rot: set if has input rotator
* @has_out_rot: set if has output rotator
@@ -386,7 +376,7 @@ struct fimc_pix_limit {
* @min_vsize_align: minimum vertical pixel size alignment
* @out_buf_count: the number of buffers in output DMA sequence
*/
-struct samsung_fimc_variant {
+struct fimc_variant {
unsigned int pix_hoff:1;
unsigned int has_inp_rot:1;
unsigned int has_out_rot:1;
@@ -403,23 +393,19 @@ struct samsung_fimc_variant {
};
/**
- * struct samsung_fimc_driverdata - per device type driver data for init time.
- *
- * @variant: the variant information for this driver.
- * @dev_cnt: number of fimc sub-devices available in SoC
- * @lclk_frequency: fimc bus clock frequency
+ * struct fimc_drvdata - per device type driver data
+ * @variant: variant information for this device
+ * @num_entities: number of fimc instances available in a SoC
+ * @lclk_frequency: local bus clock frequency
*/
-struct samsung_fimc_driverdata {
- struct samsung_fimc_variant *variant[FIMC_MAX_DEVS];
- unsigned long lclk_frequency;
- int num_entities;
+struct fimc_drvdata {
+ struct fimc_variant *variant[FIMC_MAX_DEVS];
+ int num_entities;
+ unsigned long lclk_frequency;
};
-struct fimc_pipeline {
- struct media_pipeline *pipe;
- struct v4l2_subdev *sensor;
- struct v4l2_subdev *csis;
-};
+#define fimc_get_drvdata(_pdev) \
+ ((struct fimc_drvdata *) platform_get_device_id(_pdev)->driver_data)
struct fimc_ctx;
@@ -431,10 +417,8 @@ struct fimc_ctx;
* @pdata: pointer to the device platform data
* @variant: the IP variant information
* @id: FIMC device index (0..FIMC_MAX_DEVS)
- * @num_clocks: the number of clocks managed by this device instance
* @clock: clocks required for FIMC operation
* @regs: the mapped hardware registers
- * @irq: FIMC interrupt number
* @irq_queue: interrupt handler waitqueue
* @v4l2_dev: root v4l2_device
* @m2m: memory-to-memory V4L2 device information
@@ -448,12 +432,10 @@ struct fimc_dev {
struct mutex lock;
struct platform_device *pdev;
struct s5p_platform_fimc *pdata;
- struct samsung_fimc_variant *variant;
+ struct fimc_variant *variant;
u16 id;
- u16 num_clocks;
struct clk *clock[MAX_FIMC_CLOCKS];
void __iomem *regs;
- int irq;
wait_queue_head_t irq_queue;
struct v4l2_device *v4l2_dev;
struct fimc_m2m_device m2m;
@@ -464,8 +446,31 @@ struct fimc_dev {
};
/**
+ * struct fimc_ctrls - v4l2 controls structure
+ * @handler: the control handler
+ * @colorfx: image effect control
+ * @colorfx_cbcr: Cb/Cr coefficients control
+ * @rotate: image rotation control
+ * @hflip: horizontal flip control
+ * @vflip: vertical flip control
+ * @alpha: RGB alpha control
+ * @ready: true if @handler is initialized
+ */
+struct fimc_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct {
+ struct v4l2_ctrl *colorfx;
+ struct v4l2_ctrl *colorfx_cbcr;
+ };
+ struct v4l2_ctrl *rotate;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *alpha;
+ bool ready;
+};
+
+/**
* fimc_ctx - the device context data
- * @slock: spinlock protecting this data structure
* @s_frame: source frame properties
* @d_frame: destination frame properties
* @out_order_1p: output 1-plane YCBCR order
@@ -484,15 +489,9 @@ struct fimc_dev {
* @fimc_dev: the FIMC device this context applies to
* @m2m_ctx: memory-to-memory device context
* @fh: v4l2 file handle
- * @ctrl_handler: v4l2 controls handler
- * @ctrl_rotate image rotation control
- * @ctrl_hflip horizontal flip control
- * @ctrl_vflip vertical flip control
- * @ctrl_alpha RGB alpha control
- * @ctrls_rdy: true if the control handler is initialized
+ * @ctrls: v4l2 controls structure
*/
struct fimc_ctx {
- spinlock_t slock;
struct fimc_frame s_frame;
struct fimc_frame d_frame;
u32 out_order_1p;
@@ -511,12 +510,7 @@ struct fimc_ctx {
struct fimc_dev *fimc_dev;
struct v4l2_m2m_ctx *m2m_ctx;
struct v4l2_fh fh;
- struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_ctrl *ctrl_rotate;
- struct v4l2_ctrl *ctrl_hflip;
- struct v4l2_ctrl *ctrl_vflip;
- struct v4l2_ctrl *ctrl_alpha;
- bool ctrls_rdy;
+ struct fimc_ctrls ctrls;
};
#define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh)
@@ -560,13 +554,13 @@ static inline bool fimc_capture_active(struct fimc_dev *fimc)
return ret;
}
-static inline void fimc_ctx_state_lock_set(u32 state, struct fimc_ctx *ctx)
+static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx)
{
unsigned long flags;
- spin_lock_irqsave(&ctx->slock, flags);
+ spin_lock_irqsave(&ctx->fimc_dev->slock, flags);
ctx->state |= state;
- spin_unlock_irqrestore(&ctx->slock, flags);
+ spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);
}
static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx)
@@ -574,9 +568,9 @@ static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx)
unsigned long flags;
bool ret;
- spin_lock_irqsave(&ctx->slock, flags);
+ spin_lock_irqsave(&ctx->fimc_dev->slock, flags);
ret = (ctx->state & mask) == mask;
- spin_unlock_irqrestore(&ctx->slock, flags);
+ spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags);
return ret;
}
@@ -589,61 +583,13 @@ static inline int tiled_fmt(struct fimc_fmt *fmt)
static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt)
{
switch (fmt->color) {
- case S5P_FIMC_RGB444: return 0x0f;
- case S5P_FIMC_RGB555: return 0x01;
- case S5P_FIMC_RGB888: return 0xff;
+ case FIMC_FMT_RGB444: return 0x0f;
+ case FIMC_FMT_RGB555: return 0x01;
+ case FIMC_FMT_RGB888: return 0xff;
default: return 0;
};
}
-static inline void fimc_hw_clear_irq(struct fimc_dev *dev)
-{
- u32 cfg = readl(dev->regs + S5P_CIGCTRL);
- cfg |= S5P_CIGCTRL_IRQ_CLR;
- writel(cfg, dev->regs + S5P_CIGCTRL);
-}
-
-static inline void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on)
-{
- u32 cfg = readl(dev->regs + S5P_CISCCTRL);
- if (on)
- cfg |= S5P_CISCCTRL_SCALERSTART;
- else
- cfg &= ~S5P_CISCCTRL_SCALERSTART;
- writel(cfg, dev->regs + S5P_CISCCTRL);
-}
-
-static inline void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on)
-{
- u32 cfg = readl(dev->regs + S5P_MSCTRL);
- if (on)
- cfg |= S5P_MSCTRL_ENVID;
- else
- cfg &= ~S5P_MSCTRL_ENVID;
- writel(cfg, dev->regs + S5P_MSCTRL);
-}
-
-static inline void fimc_hw_dis_capture(struct fimc_dev *dev)
-{
- u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
- cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC);
- writel(cfg, dev->regs + S5P_CIIMGCPT);
-}
-
-/**
- * fimc_hw_set_dma_seq - configure output DMA buffer sequence
- * @mask: each bit corresponds to one of 32 output buffer registers set
- * 1 to include buffer in the sequence, 0 to disable
- *
- * This function mask output DMA ring buffers, i.e. it allows to configure
- * which of the output buffer address registers will be used by the DMA
- * engine.
- */
-static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask)
-{
- writel(mask, dev->regs + S5P_CIFCNTSEQ);
-}
-
static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
enum v4l2_buf_type type)
{
@@ -665,48 +611,6 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx,
return frame;
}
-/* Return an index to the buffer actually being written. */
-static inline u32 fimc_hw_get_frame_index(struct fimc_dev *dev)
-{
- u32 reg;
-
- if (dev->variant->has_cistatus2) {
- reg = readl(dev->regs + S5P_CISTATUS2) & 0x3F;
- return reg > 0 ? --reg : reg;
- } else {
- reg = readl(dev->regs + S5P_CISTATUS);
- return (reg & S5P_CISTATUS_FRAMECNT_MASK) >>
- S5P_CISTATUS_FRAMECNT_SHIFT;
- }
-}
-
-/* -----------------------------------------------------*/
-/* fimc-reg.c */
-void fimc_hw_reset(struct fimc_dev *fimc);
-void fimc_hw_set_rotation(struct fimc_ctx *ctx);
-void fimc_hw_set_target_format(struct fimc_ctx *ctx);
-void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
-void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable);
-void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
-void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
-void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
-void fimc_hw_en_capture(struct fimc_ctx *ctx);
-void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active);
-void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx);
-void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
-void fimc_hw_set_input_path(struct fimc_ctx *ctx);
-void fimc_hw_set_output_path(struct fimc_ctx *ctx);
-void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr);
-void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr,
- int index);
-int fimc_hw_set_camera_source(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam);
-int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f);
-int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam);
-int fimc_hw_set_camera_type(struct fimc_dev *fimc,
- struct s5p_fimc_isp_info *cam);
-
/* -----------------------------------------------------*/
/* fimc-core.c */
int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv,
@@ -720,6 +624,7 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height,
struct v4l2_pix_format_mplane *pix);
struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code,
unsigned int mask, int index);
+struct fimc_fmt *fimc_get_format(unsigned int index);
int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh,
int dw, int dh, int rotation);
@@ -730,7 +635,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb,
void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f);
void fimc_set_yuv_order(struct fimc_ctx *ctx);
void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f);
-void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done);
+void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf);
int fimc_register_m2m_device(struct fimc_dev *fimc,
struct v4l2_device *v4l2_dev);
@@ -739,33 +644,18 @@ int fimc_register_driver(void);
void fimc_unregister_driver(void);
/* -----------------------------------------------------*/
+/* fimc-m2m.c */
+void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state);
+
+/* -----------------------------------------------------*/
/* fimc-capture.c */
-int fimc_register_capture_device(struct fimc_dev *fimc,
- struct v4l2_device *v4l2_dev);
-void fimc_unregister_capture_device(struct fimc_dev *fimc);
+int fimc_initialize_capture_subdev(struct fimc_dev *fimc);
+void fimc_unregister_capture_subdev(struct fimc_dev *fimc);
int fimc_capture_ctrls_create(struct fimc_dev *fimc);
-int fimc_vid_cap_buf_queue(struct fimc_dev *fimc,
- struct fimc_vid_buffer *fimc_vb);
void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification,
void *arg);
int fimc_capture_suspend(struct fimc_dev *fimc);
int fimc_capture_resume(struct fimc_dev *fimc);
-int fimc_capture_config_update(struct fimc_ctx *ctx);
-
-/* Locking: the caller holds fimc->slock */
-static inline void fimc_activate_capture(struct fimc_ctx *ctx)
-{
- fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled);
- fimc_hw_en_capture(ctx);
-}
-
-static inline void fimc_deactivate_capture(struct fimc_dev *fimc)
-{
- fimc_hw_en_lastirq(fimc, true);
- fimc_hw_dis_capture(fimc);
- fimc_hw_enable_scaler(fimc, false);
- fimc_hw_en_lastirq(fimc, false);
-}
/*
* Buffer list manipulation functions. Must be called with fimc.slock held.
diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.c b/drivers/media/video/s5p-fimc/fimc-lite-reg.c
new file mode 100644
index 00000000000..419adfb7cdf
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.c
@@ -0,0 +1,300 @@
+/*
+ * Register interface file for EXYNOS FIMC-LITE (camera interface) driver
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <media/s5p_fimc.h>
+
+#include "fimc-lite-reg.h"
+#include "fimc-lite.h"
+#include "fimc-core.h"
+
+#define FLITE_RESET_TIMEOUT 50 /* in ms */
+
+void flite_hw_reset(struct fimc_lite *dev)
+{
+ unsigned long end = jiffies + msecs_to_jiffies(FLITE_RESET_TIMEOUT);
+ u32 cfg;
+
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ cfg |= FLITE_REG_CIGCTRL_SWRST_REQ;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ while (time_is_after_jiffies(end)) {
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ if (cfg & FLITE_REG_CIGCTRL_SWRST_RDY)
+ break;
+ usleep_range(1000, 5000);
+ }
+
+ cfg |= FLITE_REG_CIGCTRL_SWRST;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+}
+
+void flite_hw_clear_pending_irq(struct fimc_lite *dev)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS);
+ cfg &= ~FLITE_REG_CISTATUS_IRQ_CAM;
+ writel(cfg, dev->regs + FLITE_REG_CISTATUS);
+}
+
+u32 flite_hw_get_interrupt_source(struct fimc_lite *dev)
+{
+ u32 intsrc = readl(dev->regs + FLITE_REG_CISTATUS);
+ return intsrc & FLITE_REG_CISTATUS_IRQ_MASK;
+}
+
+void flite_hw_clear_last_capture_end(struct fimc_lite *dev)
+{
+
+ u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS2);
+ cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND;
+ writel(cfg, dev->regs + FLITE_REG_CISTATUS2);
+}
+
+void flite_hw_set_interrupt_mask(struct fimc_lite *dev)
+{
+ u32 cfg, intsrc;
+
+ /* Select interrupts to be enabled for each output mode */
+ if (dev->out_path == FIMC_IO_DMA) {
+ intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
+ FLITE_REG_CIGCTRL_IRQ_LASTEN |
+ FLITE_REG_CIGCTRL_IRQ_STARTEN;
+ } else {
+ /* An output to the FIMC-IS */
+ intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN |
+ FLITE_REG_CIGCTRL_IRQ_LASTEN;
+ }
+
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ cfg |= FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK;
+ cfg &= ~intsrc;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+}
+
+void flite_hw_capture_start(struct fimc_lite *dev)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT);
+ cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN;
+ writel(cfg, dev->regs + FLITE_REG_CIIMGCPT);
+}
+
+void flite_hw_capture_stop(struct fimc_lite *dev)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT);
+ cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN;
+ writel(cfg, dev->regs + FLITE_REG_CIIMGCPT);
+}
+
+/*
+ * Test pattern (color bars) enable/disable. External sensor
+ * pixel clock must be active for the test pattern to work.
+ */
+void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ if (on)
+ cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR;
+ else
+ cfg &= ~FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+}
+
+static const u32 src_pixfmt_map[8][3] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY,
+ FLITE_REG_CIGCTRL_YUV422_1P },
+ { V4L2_PIX_FMT_SGRBG8, 0, FLITE_REG_CIGCTRL_RAW8 },
+ { V4L2_PIX_FMT_SGRBG10, 0, FLITE_REG_CIGCTRL_RAW10 },
+ { V4L2_PIX_FMT_SGRBG12, 0, FLITE_REG_CIGCTRL_RAW12 },
+ { V4L2_MBUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) },
+};
+
+/* Set camera input pixel format and resolution */
+void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f)
+{
+ enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code;
+ unsigned int i = ARRAY_SIZE(src_pixfmt_map);
+ u32 cfg;
+
+ while (i-- >= 0) {
+ if (src_pixfmt_map[i][0] == pixelcode)
+ break;
+ }
+
+ if (i == 0 && src_pixfmt_map[i][0] != pixelcode) {
+ v4l2_err(dev->vfd,
+ "Unsupported pixel code, falling back to %#08x\n",
+ src_pixfmt_map[i][0]);
+ }
+
+ cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ cfg &= ~FLITE_REG_CIGCTRL_FMT_MASK;
+ cfg |= src_pixfmt_map[i][2];
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ cfg = readl(dev->regs + FLITE_REG_CISRCSIZE);
+ cfg &= ~(FLITE_REG_CISRCSIZE_ORDER422_MASK |
+ FLITE_REG_CISRCSIZE_SIZE_CAM_MASK);
+ cfg |= (f->f_width << 16) | f->f_height;
+ cfg |= src_pixfmt_map[i][1];
+ writel(cfg, dev->regs + FLITE_REG_CISRCSIZE);
+}
+
+/* Set the camera host input window offsets (cropping) */
+void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f)
+{
+ u32 hoff2, voff2;
+ u32 cfg;
+
+ cfg = readl(dev->regs + FLITE_REG_CIWDOFST);
+ cfg &= ~FLITE_REG_CIWDOFST_OFST_MASK;
+ cfg |= (f->rect.left << 16) | f->rect.top;
+ cfg |= FLITE_REG_CIWDOFST_WINOFSEN;
+ writel(cfg, dev->regs + FLITE_REG_CIWDOFST);
+
+ hoff2 = f->f_width - f->rect.width - f->rect.left;
+ voff2 = f->f_height - f->rect.height - f->rect.top;
+
+ cfg = (hoff2 << 16) | voff2;
+ writel(cfg, dev->regs + FLITE_REG_CIWDOFST2);
+}
+
+/* Select camera port (A, B) */
+static void flite_hw_set_camera_port(struct fimc_lite *dev, int id)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGENERAL);
+ if (id == 0)
+ cfg &= ~FLITE_REG_CIGENERAL_CAM_B;
+ else
+ cfg |= FLITE_REG_CIGENERAL_CAM_B;
+ writel(cfg, dev->regs + FLITE_REG_CIGENERAL);
+}
+
+/* Select serial or parallel bus, camera port (A,B) and set signals polarity */
+void flite_hw_set_camera_bus(struct fimc_lite *dev,
+ struct s5p_fimc_isp_info *s_info)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+ unsigned int flags = s_info->flags;
+
+ if (s_info->bus_type != FIMC_MIPI_CSI2) {
+ cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI |
+ FLITE_REG_CIGCTRL_INVPOLPCLK |
+ FLITE_REG_CIGCTRL_INVPOLVSYNC |
+ FLITE_REG_CIGCTRL_INVPOLHREF);
+
+ if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+ cfg |= FLITE_REG_CIGCTRL_INVPOLPCLK;
+
+ if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ cfg |= FLITE_REG_CIGCTRL_INVPOLVSYNC;
+
+ if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ cfg |= FLITE_REG_CIGCTRL_INVPOLHREF;
+ } else {
+ cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI;
+ }
+
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ flite_hw_set_camera_port(dev, s_info->mux_id);
+}
+
+void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f)
+{
+ static const u32 pixcode[4][2] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR },
+ { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB },
+ { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY },
+ { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY },
+ };
+ u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT);
+ unsigned int i = ARRAY_SIZE(pixcode);
+
+ while (i-- >= 0)
+ if (pixcode[i][0] == dev->fmt->mbus_code)
+ break;
+ cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK;
+ writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT);
+}
+
+void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f)
+{
+ u32 cfg;
+
+ /* Maximum output pixel size */
+ cfg = readl(dev->regs + FLITE_REG_CIOCAN);
+ cfg &= ~FLITE_REG_CIOCAN_MASK;
+ cfg = (f->f_height << 16) | f->f_width;
+ writel(cfg, dev->regs + FLITE_REG_CIOCAN);
+
+ /* DMA offsets */
+ cfg = readl(dev->regs + FLITE_REG_CIOOFF);
+ cfg &= ~FLITE_REG_CIOOFF_MASK;
+ cfg |= (f->rect.top << 16) | f->rect.left;
+ writel(cfg, dev->regs + FLITE_REG_CIOOFF);
+}
+
+/* Enable/disable output DMA, set output pixel size and offsets (composition) */
+void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
+ bool enable)
+{
+ u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL);
+
+ if (!enable) {
+ cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+ return;
+ }
+
+ cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE;
+ writel(cfg, dev->regs + FLITE_REG_CIGCTRL);
+
+ flite_hw_set_out_order(dev, f);
+ flite_hw_set_dma_window(dev, f);
+}
+
+void flite_hw_dump_regs(struct fimc_lite *dev, const char *label)
+{
+ struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { 0x00, "CISRCSIZE" },
+ { 0x04, "CIGCTRL" },
+ { 0x08, "CIIMGCPT" },
+ { 0x0c, "CICPTSEQ" },
+ { 0x10, "CIWDOFST" },
+ { 0x14, "CIWDOFST2" },
+ { 0x18, "CIODMAFMT" },
+ { 0x20, "CIOCAN" },
+ { 0x24, "CIOOFF" },
+ { 0x30, "CIOSA" },
+ { 0x40, "CISTATUS" },
+ { 0x44, "CISTATUS2" },
+ { 0xf0, "CITHOLD" },
+ { 0xfc, "CIGENERAL" },
+ };
+ u32 i;
+
+ pr_info("--- %s ---\n", label);
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ u32 cfg = readl(dev->regs + registers[i].offset);
+ pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg);
+ }
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.h b/drivers/media/video/s5p-fimc/fimc-lite-reg.h
new file mode 100644
index 00000000000..adb9e9e6f3c
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_LITE_REG_H_
+#define FIMC_LITE_REG_H_
+
+#include "fimc-lite.h"
+
+/* Camera Source size */
+#define FLITE_REG_CISRCSIZE 0x00
+#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR (0 << 14)
+#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB (1 << 14)
+#define FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY (2 << 14)
+#define FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY (3 << 14)
+#define FLITE_REG_CISRCSIZE_ORDER422_MASK (0x3 << 14)
+#define FLITE_REG_CISRCSIZE_SIZE_CAM_MASK (0x3fff << 16 | 0x3fff)
+
+/* Global control */
+#define FLITE_REG_CIGCTRL 0x04
+#define FLITE_REG_CIGCTRL_YUV422_1P (0x1e << 24)
+#define FLITE_REG_CIGCTRL_RAW8 (0x2a << 24)
+#define FLITE_REG_CIGCTRL_RAW10 (0x2b << 24)
+#define FLITE_REG_CIGCTRL_RAW12 (0x2c << 24)
+#define FLITE_REG_CIGCTRL_RAW14 (0x2d << 24)
+/* User defined formats. x = 0...15 */
+#define FLITE_REG_CIGCTRL_USER(x) ((0x30 + x - 1) << 24)
+#define FLITE_REG_CIGCTRL_FMT_MASK (0x3f << 24)
+#define FLITE_REG_CIGCTRL_SHADOWMASK_DISABLE (1 << 21)
+#define FLITE_REG_CIGCTRL_ODMA_DISABLE (1 << 20)
+#define FLITE_REG_CIGCTRL_SWRST_REQ (1 << 19)
+#define FLITE_REG_CIGCTRL_SWRST_RDY (1 << 18)
+#define FLITE_REG_CIGCTRL_SWRST (1 << 17)
+#define FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR (1 << 15)
+#define FLITE_REG_CIGCTRL_INVPOLPCLK (1 << 14)
+#define FLITE_REG_CIGCTRL_INVPOLVSYNC (1 << 13)
+#define FLITE_REG_CIGCTRL_INVPOLHREF (1 << 12)
+/* Interrupts mask bits (1 disables an interrupt) */
+#define FLITE_REG_CIGCTRL_IRQ_LASTEN (1 << 8)
+#define FLITE_REG_CIGCTRL_IRQ_ENDEN (1 << 7)
+#define FLITE_REG_CIGCTRL_IRQ_STARTEN (1 << 6)
+#define FLITE_REG_CIGCTRL_IRQ_OVFEN (1 << 5)
+#define FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK (0xf << 5)
+#define FLITE_REG_CIGCTRL_SELCAM_MIPI (1 << 3)
+
+/* Image Capture Enable */
+#define FLITE_REG_CIIMGCPT 0x08
+#define FLITE_REG_CIIMGCPT_IMGCPTEN (1 << 31)
+#define FLITE_REG_CIIMGCPT_CPT_FREN (1 << 25)
+#define FLITE_REG_CIIMGCPT_CPT_MOD_FRCNT (1 << 18)
+#define FLITE_REG_CIIMGCPT_CPT_MOD_FREN (0 << 18)
+
+/* Capture Sequence */
+#define FLITE_REG_CICPTSEQ 0x0c
+
+/* Camera Window Offset */
+#define FLITE_REG_CIWDOFST 0x10
+#define FLITE_REG_CIWDOFST_WINOFSEN (1 << 31)
+#define FLITE_REG_CIWDOFST_CLROVIY (1 << 31)
+#define FLITE_REG_CIWDOFST_CLROVFICB (1 << 15)
+#define FLITE_REG_CIWDOFST_CLROVFICR (1 << 14)
+#define FLITE_REG_CIWDOFST_OFST_MASK ((0x1fff << 16) | 0x1fff)
+
+/* Camera Window Offset2 */
+#define FLITE_REG_CIWDOFST2 0x14
+
+/* Camera Output DMA Format */
+#define FLITE_REG_CIODMAFMT 0x18
+#define FLITE_REG_CIODMAFMT_RAW_CON (1 << 15)
+#define FLITE_REG_CIODMAFMT_PACK12 (1 << 14)
+#define FLITE_REG_CIODMAFMT_CRYCBY (0 << 4)
+#define FLITE_REG_CIODMAFMT_CBYCRY (1 << 4)
+#define FLITE_REG_CIODMAFMT_YCRYCB (2 << 4)
+#define FLITE_REG_CIODMAFMT_YCBYCR (3 << 4)
+#define FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK (0x3 << 4)
+
+/* Camera Output Canvas */
+#define FLITE_REG_CIOCAN 0x20
+#define FLITE_REG_CIOCAN_MASK ((0x3fff << 16) | 0x3fff)
+
+/* Camera Output DMA Offset */
+#define FLITE_REG_CIOOFF 0x24
+#define FLITE_REG_CIOOFF_MASK ((0x3fff << 16) | 0x3fff)
+
+/* Camera Output DMA Start Address */
+#define FLITE_REG_CIOSA 0x30
+
+/* Camera Status */
+#define FLITE_REG_CISTATUS 0x40
+#define FLITE_REG_CISTATUS_MIPI_VVALID (1 << 22)
+#define FLITE_REG_CISTATUS_MIPI_HVALID (1 << 21)
+#define FLITE_REG_CISTATUS_MIPI_DVALID (1 << 20)
+#define FLITE_REG_CISTATUS_ITU_VSYNC (1 << 14)
+#define FLITE_REG_CISTATUS_ITU_HREFF (1 << 13)
+#define FLITE_REG_CISTATUS_OVFIY (1 << 10)
+#define FLITE_REG_CISTATUS_OVFICB (1 << 9)
+#define FLITE_REG_CISTATUS_OVFICR (1 << 8)
+#define FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW (1 << 7)
+#define FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND (1 << 6)
+#define FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART (1 << 5)
+#define FLITE_REG_CISTATUS_IRQ_SRC_FRMEND (1 << 4)
+#define FLITE_REG_CISTATUS_IRQ_CAM (1 << 0)
+#define FLITE_REG_CISTATUS_IRQ_MASK (0xf << 4)
+
+/* Camera Status2 */
+#define FLITE_REG_CISTATUS2 0x44
+#define FLITE_REG_CISTATUS2_LASTCAPEND (1 << 1)
+#define FLITE_REG_CISTATUS2_FRMEND (1 << 0)
+
+/* Qos Threshold */
+#define FLITE_REG_CITHOLD 0xf0
+#define FLITE_REG_CITHOLD_W_QOS_EN (1 << 30)
+
+/* Camera General Purpose */
+#define FLITE_REG_CIGENERAL 0xfc
+/* b0: 1 - camera B, 0 - camera A */
+#define FLITE_REG_CIGENERAL_CAM_B (1 << 0)
+
+/* ----------------------------------------------------------------------------
+ * Function declarations
+ */
+void flite_hw_reset(struct fimc_lite *dev);
+void flite_hw_clear_pending_irq(struct fimc_lite *dev);
+u32 flite_hw_get_interrupt_source(struct fimc_lite *dev);
+void flite_hw_clear_last_capture_end(struct fimc_lite *dev);
+void flite_hw_set_interrupt_mask(struct fimc_lite *dev);
+void flite_hw_capture_start(struct fimc_lite *dev);
+void flite_hw_capture_stop(struct fimc_lite *dev);
+void flite_hw_set_camera_bus(struct fimc_lite *dev,
+ struct s5p_fimc_isp_info *s_info);
+void flite_hw_set_camera_polarity(struct fimc_lite *dev,
+ struct s5p_fimc_isp_info *cam);
+void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f);
+void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f);
+
+void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f,
+ bool enable);
+void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f);
+void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on);
+void flite_hw_dump_regs(struct fimc_lite *dev, const char *label);
+
+static inline void flite_hw_set_output_addr(struct fimc_lite *dev, u32 paddr)
+{
+ writel(paddr, dev->regs + FLITE_REG_CIOSA);
+}
+#endif /* FIMC_LITE_REG_H */
diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c
new file mode 100644
index 00000000000..400d701aef0
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-lite.c
@@ -0,0 +1,1576 @@
+/*
+ * Samsung EXYNOS FIMC-LITE (camera host interface) driver
+*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-mdevice.h"
+#include "fimc-core.h"
+#include "fimc-lite-reg.h"
+
+static int debug;
+module_param(debug, int, 0644);
+
+static const struct fimc_fmt fimc_lite_formats[] = {
+ {
+ .name = "YUV 4:2:2 packed, YCbYCr",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = { 16 },
+ .color = FIMC_FMT_YCBYCR422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, CbYCrY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = { 16 },
+ .color = FIMC_FMT_CBYCRY422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, CrYCbY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = { 16 },
+ .color = FIMC_FMT_CRYCBY422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,
+ }, {
+ .name = "YUV 4:2:2 packed, YCrYCb",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = { 16 },
+ .color = FIMC_FMT_YCRYCB422,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,
+ }, {
+ .name = "RAW8 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .depth = { 8 },
+ .color = FIMC_FMT_RAW8,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8,
+ }, {
+ .name = "RAW10 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .depth = { 10 },
+ .color = FIMC_FMT_RAW10,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
+ }, {
+ .name = "RAW12 (GRBG)",
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .depth = { 12 },
+ .color = FIMC_FMT_RAW12,
+ .memplanes = 1,
+ .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
+ },
+};
+
+/**
+ * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code
+ * @pixelformat: fourcc to match, ignored if null
+ * @mbus_code: media bus code to match, ignored if null
+ * @index: index to the fimc_lite_formats array, ignored if negative
+ */
+static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat,
+ const u32 *mbus_code, int index)
+{
+ const struct fimc_fmt *fmt, *def_fmt = NULL;
+ unsigned int i;
+ int id = 0;
+
+ if (index >= (int)ARRAY_SIZE(fimc_lite_formats))
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) {
+ fmt = &fimc_lite_formats[i];
+ if (pixelformat && fmt->fourcc == *pixelformat)
+ return fmt;
+ if (mbus_code && fmt->mbus_code == *mbus_code)
+ return fmt;
+ if (index == id)
+ def_fmt = fmt;
+ id++;
+ }
+ return def_fmt;
+}
+
+static int fimc_lite_hw_init(struct fimc_lite *fimc)
+{
+ struct fimc_pipeline *pipeline = &fimc->pipeline;
+ struct fimc_sensor_info *sensor;
+ unsigned long flags;
+
+ if (pipeline->subdevs[IDX_SENSOR] == NULL)
+ return -ENXIO;
+
+ if (fimc->fmt == NULL)
+ return -EINVAL;
+
+ sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]);
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ flite_hw_set_camera_bus(fimc, sensor->pdata);
+ flite_hw_set_source_format(fimc, &fimc->inp_frame);
+ flite_hw_set_window_offset(fimc, &fimc->inp_frame);
+ flite_hw_set_output_dma(fimc, &fimc->out_frame, true);
+ flite_hw_set_interrupt_mask(fimc);
+ flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
+
+ if (debug > 0)
+ flite_hw_dump_regs(fimc, __func__);
+
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+}
+
+/*
+ * Reinitialize the driver so it is ready to start the streaming again.
+ * Set fimc->state to indicate stream off and the hardware shut down state.
+ * If not suspending (@suspend is false), return any buffers to videobuf2.
+ * Otherwise put any owned buffers onto the pending buffers queue, so they
+ * can be re-spun when the device is being resumed. Also perform FIMC
+ * software reset and disable streaming on the whole pipeline if required.
+ */
+static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend)
+{
+ struct flite_buffer *buf;
+ unsigned long flags;
+ bool streaming;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ streaming = fimc->state & (1 << ST_SENSOR_STREAM);
+
+ fimc->state &= ~(1 << ST_FLITE_RUN | 1 << ST_FLITE_OFF |
+ 1 << ST_FLITE_STREAM | 1 << ST_SENSOR_STREAM);
+ if (suspend)
+ fimc->state |= (1 << ST_FLITE_SUSPENDED);
+ else
+ fimc->state &= ~(1 << ST_FLITE_PENDING |
+ 1 << ST_FLITE_SUSPENDED);
+
+ /* Release unused buffers */
+ while (!suspend && !list_empty(&fimc->pending_buf_q)) {
+ buf = fimc_lite_pending_queue_pop(fimc);
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+ /* If suspending put unused buffers onto pending queue */
+ while (!list_empty(&fimc->active_buf_q)) {
+ buf = fimc_lite_active_queue_pop(fimc);
+ if (suspend)
+ fimc_lite_pending_queue_add(fimc, buf);
+ else
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ }
+
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ flite_hw_reset(fimc);
+
+ if (!streaming)
+ return 0;
+
+ return fimc_pipeline_s_stream(&fimc->pipeline, 0);
+}
+
+static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend)
+{
+ unsigned long flags;
+
+ if (!fimc_lite_active(fimc))
+ return 0;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ set_bit(ST_FLITE_OFF, &fimc->state);
+ flite_hw_capture_stop(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ wait_event_timeout(fimc->irq_queue,
+ !test_bit(ST_FLITE_OFF, &fimc->state),
+ (2*HZ/10)); /* 200 ms */
+
+ return fimc_lite_reinit(fimc, suspend);
+}
+
+/* Must be called with fimc.slock spinlock held. */
+static void fimc_lite_config_update(struct fimc_lite *fimc)
+{
+ flite_hw_set_window_offset(fimc, &fimc->inp_frame);
+ flite_hw_set_dma_window(fimc, &fimc->out_frame);
+ flite_hw_set_test_pattern(fimc, fimc->test_pattern->val);
+ clear_bit(ST_FLITE_CONFIG, &fimc->state);
+}
+
+static irqreturn_t flite_irq_handler(int irq, void *priv)
+{
+ struct fimc_lite *fimc = priv;
+ struct flite_buffer *vbuf;
+ unsigned long flags;
+ struct timeval *tv;
+ struct timespec ts;
+ u32 intsrc;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ intsrc = flite_hw_get_interrupt_source(fimc);
+ flite_hw_clear_pending_irq(fimc);
+
+ if (test_and_clear_bit(ST_FLITE_OFF, &fimc->state)) {
+ wake_up(&fimc->irq_queue);
+ goto done;
+ }
+
+ if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW) {
+ clear_bit(ST_FLITE_RUN, &fimc->state);
+ fimc->events.data_overflow++;
+ }
+
+ if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND) {
+ flite_hw_clear_last_capture_end(fimc);
+ clear_bit(ST_FLITE_STREAM, &fimc->state);
+ wake_up(&fimc->irq_queue);
+ }
+
+ if (fimc->out_path != FIMC_IO_DMA)
+ goto done;
+
+ if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) &&
+ test_bit(ST_FLITE_RUN, &fimc->state) &&
+ !list_empty(&fimc->active_buf_q) &&
+ !list_empty(&fimc->pending_buf_q)) {
+ vbuf = fimc_lite_active_queue_pop(fimc);
+ ktime_get_ts(&ts);
+ tv = &vbuf->vb.v4l2_buf.timestamp;
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC;
+ vbuf->vb.v4l2_buf.sequence = fimc->frame_count++;
+ vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE);
+
+ vbuf = fimc_lite_pending_queue_pop(fimc);
+ flite_hw_set_output_addr(fimc, vbuf->paddr);
+ fimc_lite_active_queue_add(fimc, vbuf);
+ }
+
+ if (test_bit(ST_FLITE_CONFIG, &fimc->state))
+ fimc_lite_config_update(fimc);
+
+ if (list_empty(&fimc->pending_buf_q)) {
+ flite_hw_capture_stop(fimc);
+ clear_bit(ST_FLITE_STREAM, &fimc->state);
+ }
+done:
+ set_bit(ST_FLITE_RUN, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return IRQ_HANDLED;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_lite *fimc = q->drv_priv;
+ int ret;
+
+ fimc->frame_count = 0;
+
+ ret = fimc_lite_hw_init(fimc);
+ if (ret) {
+ fimc_lite_reinit(fimc, false);
+ return ret;
+ }
+
+ set_bit(ST_FLITE_PENDING, &fimc->state);
+
+ if (!list_empty(&fimc->active_buf_q) &&
+ !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) {
+ flite_hw_capture_start(fimc);
+
+ if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(&fimc->pipeline, 1);
+ }
+ if (debug > 0)
+ flite_hw_dump_regs(fimc, __func__);
+
+ return 0;
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_lite *fimc = q->drv_priv;
+
+ if (!fimc_lite_active(fimc))
+ return -EINVAL;
+
+ return fimc_lite_stop_capture(fimc, false);
+}
+
+static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ struct fimc_lite *fimc = vq->drv_priv;
+ struct flite_frame *frame = &fimc->out_frame;
+ const struct fimc_fmt *fmt = fimc->fmt;
+ unsigned long wh;
+ int i;
+
+ if (pfmt) {
+ pixm = &pfmt->fmt.pix_mp;
+ fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1);
+ wh = pixm->width * pixm->height;
+ } else {
+ wh = frame->f_width * frame->f_height;
+ }
+
+ if (fmt == NULL)
+ return -EINVAL;
+
+ *num_planes = fmt->memplanes;
+
+ for (i = 0; i < fmt->memplanes; i++) {
+ unsigned int size = (wh * fmt->depth[i]) / 8;
+ if (pixm)
+ sizes[i] = max(size, pixm->plane_fmt[i].sizeimage);
+ else
+ sizes[i] = size;
+ allocators[i] = fimc->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_lite *fimc = vq->drv_priv;
+ int i;
+
+ if (fimc->fmt == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < fimc->fmt->memplanes; i++) {
+ unsigned long size = fimc->payload[i];
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(fimc->vfd,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static void buffer_queue(struct vb2_buffer *vb)
+{
+ struct flite_buffer *buf
+ = container_of(vb, struct flite_buffer, vb);
+ struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) &&
+ !test_bit(ST_FLITE_STREAM, &fimc->state) &&
+ list_empty(&fimc->active_buf_q)) {
+ flite_hw_set_output_addr(fimc, buf->paddr);
+ fimc_lite_active_queue_add(fimc, buf);
+ } else {
+ fimc_lite_pending_queue_add(fimc, buf);
+ }
+
+ if (vb2_is_streaming(&fimc->vb_queue) &&
+ !list_empty(&fimc->pending_buf_q) &&
+ !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) {
+ flite_hw_capture_start(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state))
+ fimc_pipeline_s_stream(&fimc->pipeline, 1);
+ return;
+ }
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static void fimc_lock(struct vb2_queue *vq)
+{
+ struct fimc_lite *fimc = vb2_get_drv_priv(vq);
+ mutex_lock(&fimc->lock);
+}
+
+static void fimc_unlock(struct vb2_queue *vq)
+{
+ struct fimc_lite *fimc = vb2_get_drv_priv(vq);
+ mutex_unlock(&fimc->lock);
+}
+
+static const struct vb2_ops fimc_lite_qops = {
+ .queue_setup = queue_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+};
+
+static void fimc_lite_clear_event_counters(struct fimc_lite *fimc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ memset(&fimc->events, 0, sizeof(fimc->events));
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static int fimc_lite_open(struct file *file)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ int ret = v4l2_fh_open(file);
+
+ if (ret)
+ return ret;
+
+ set_bit(ST_FLITE_IN_USE, &fimc->state);
+ pm_runtime_get_sync(&fimc->pdev->dev);
+
+ if (++fimc->ref_count != 1 || fimc->out_path != FIMC_IO_DMA)
+ return ret;
+
+ ret = fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity,
+ true);
+ if (ret < 0) {
+ v4l2_err(fimc->vfd, "Video pipeline initialization failed\n");
+ pm_runtime_put_sync(&fimc->pdev->dev);
+ fimc->ref_count--;
+ v4l2_fh_release(file);
+ clear_bit(ST_FLITE_IN_USE, &fimc->state);
+ }
+
+ fimc_lite_clear_event_counters(fimc);
+ return ret;
+}
+
+static int fimc_lite_close(struct file *file)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) {
+ clear_bit(ST_FLITE_IN_USE, &fimc->state);
+ fimc_lite_stop_capture(fimc, false);
+ fimc_pipeline_shutdown(&fimc->pipeline);
+ clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
+ }
+
+ pm_runtime_put(&fimc->pdev->dev);
+
+ if (fimc->ref_count == 0)
+ vb2_queue_release(&fimc->vb_queue);
+
+ return v4l2_fh_release(file);
+}
+
+static unsigned int fimc_lite_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ return vb2_poll(&fimc->vb_queue, file, wait);
+}
+
+static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ return vb2_mmap(&fimc->vb_queue, vma);
+}
+
+static const struct v4l2_file_operations fimc_lite_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_lite_open,
+ .release = fimc_lite_close,
+ .poll = fimc_lite_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = fimc_lite_mmap,
+};
+
+/*
+ * Format and crop negotiation helpers
+ */
+
+static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc,
+ u32 *width, u32 *height,
+ u32 *code, u32 *fourcc, int pad)
+{
+ struct flite_variant *variant = fimc->variant;
+ const struct fimc_fmt *fmt;
+
+ fmt = fimc_lite_find_format(fourcc, code, 0);
+ if (WARN_ON(!fmt))
+ return NULL;
+
+ if (code)
+ *code = fmt->mbus_code;
+ if (fourcc)
+ *fourcc = fmt->fourcc;
+
+ if (pad == FLITE_SD_PAD_SINK) {
+ v4l_bound_align_image(width, 8, variant->max_width,
+ ffs(variant->out_width_align) - 1,
+ height, 0, variant->max_height, 0, 0);
+ } else {
+ v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width,
+ ffs(variant->out_width_align) - 1,
+ height, 0, fimc->inp_frame.rect.height,
+ 0, 0);
+ }
+
+ v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n",
+ code ? *code : 0, *width, *height);
+
+ return fmt;
+}
+
+static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r)
+{
+ struct flite_frame *frame = &fimc->inp_frame;
+
+ v4l_bound_align_image(&r->width, 0, frame->f_width, 0,
+ &r->height, 0, frame->f_height, 0, 0);
+
+ /* Adjust left/top if cropping rectangle got out of bounds */
+ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
+ r->left = round_down(r->left, fimc->variant->win_hor_offs_align);
+ r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height);
+
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d",
+ r->left, r->top, r->width, r->height,
+ frame->f_width, frame->f_height);
+}
+
+static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
+{
+ struct flite_frame *frame = &fimc->out_frame;
+ struct v4l2_rect *crop_rect = &fimc->inp_frame.rect;
+
+ /* Scaling is not supported so we enforce compose rectangle size
+ same as size of the sink crop rectangle. */
+ r->width = crop_rect->width;
+ r->height = crop_rect->height;
+
+ /* Adjust left/top if the composing rectangle got out of bounds */
+ r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width);
+ r->left = round_down(r->left, fimc->variant->out_hor_offs_align);
+ r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height);
+
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d",
+ r->left, r->top, r->width, r->height,
+ frame->f_width, frame->f_height);
+}
+
+/*
+ * Video node ioctl operations
+ */
+static int fimc_vidioc_querycap_capture(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver));
+ cap->bus_info[0] = 0;
+ cap->card[0] = 0;
+ cap->capabilities = V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ const struct fimc_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(fimc_lite_formats))
+ return -EINVAL;
+
+ fmt = &fimc_lite_formats[f->index];
+ strlcpy(f->description, fmt->name, sizeof(f->description));
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int fimc_lite_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0];
+ struct flite_frame *frame = &fimc->out_frame;
+ const struct fimc_fmt *fmt = fimc->fmt;
+
+ plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8;
+ plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height;
+
+ pixm->num_planes = fmt->memplanes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->width = frame->f_width;
+ pixm->height = frame->f_height;
+ pixm->field = V4L2_FIELD_NONE;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ return 0;
+}
+
+static int fimc_lite_try_fmt(struct fimc_lite *fimc,
+ struct v4l2_pix_format_mplane *pixm,
+ const struct fimc_fmt **ffmt)
+{
+ struct flite_variant *variant = fimc->variant;
+ u32 bpl = pixm->plane_fmt[0].bytesperline;
+ const struct fimc_fmt *fmt;
+
+ fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0);
+ if (WARN_ON(fmt == NULL))
+ return -EINVAL;
+ if (ffmt)
+ *ffmt = fmt;
+ v4l_bound_align_image(&pixm->width, 8, variant->max_width,
+ ffs(variant->out_width_align) - 1,
+ &pixm->height, 0, variant->max_height, 0, 0);
+
+ if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width))
+ pixm->plane_fmt[0].bytesperline = (pixm->width *
+ fmt->depth[0]) / 8;
+
+ if (pixm->plane_fmt[0].sizeimage == 0)
+ pixm->plane_fmt[0].sizeimage = (pixm->width * pixm->height *
+ fmt->depth[0]) / 8;
+ pixm->num_planes = fmt->memplanes;
+ pixm->pixelformat = fmt->fourcc;
+ pixm->colorspace = V4L2_COLORSPACE_JPEG;
+ pixm->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static int fimc_lite_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL);
+}
+
+static int fimc_lite_s_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct flite_frame *frame = &fimc->out_frame;
+ const struct fimc_fmt *fmt = NULL;
+ int ret;
+
+ if (vb2_is_busy(&fimc->vb_queue))
+ return -EBUSY;
+
+ ret = fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, &fmt);
+ if (ret < 0)
+ return ret;
+
+ fimc->fmt = fmt;
+ fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8,
+ pixm->plane_fmt[0].sizeimage);
+ frame->f_width = pixm->width;
+ frame->f_height = pixm->height;
+
+ return 0;
+}
+
+static int fimc_pipeline_validate(struct fimc_lite *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->subdev;
+ struct v4l2_subdev_format sink_fmt, src_fmt;
+ struct media_pad *pad;
+ int ret;
+
+ while (1) {
+ /* Retrieve format at the sink pad */
+ pad = &sd->entity.pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+ /* Don't call FIMC subdev operation to avoid nested locking */
+ if (sd == &fimc->subdev) {
+ struct flite_frame *ff = &fimc->out_frame;
+ sink_fmt.format.width = ff->f_width;
+ sink_fmt.format.height = ff->f_height;
+ sink_fmt.format.code = fimc->fmt->mbus_code;
+ } else {
+ sink_fmt.pad = pad->index;
+ sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL,
+ &sink_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+ }
+ /* Retrieve format at the source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ src_fmt.pad = pad->index;
+ src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return -EPIPE;
+
+ if (src_fmt.format.width != sink_fmt.format.width ||
+ src_fmt.format.height != sink_fmt.format.height ||
+ src_fmt.format.code != sink_fmt.format.code)
+ return -EPIPE;
+ }
+ return 0;
+}
+
+static int fimc_lite_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR];
+ struct fimc_pipeline *p = &fimc->pipeline;
+ int ret;
+
+ if (fimc_lite_active(fimc))
+ return -EBUSY;
+
+ media_entity_pipeline_start(&sensor->entity, p->m_pipeline);
+
+ ret = fimc_pipeline_validate(fimc);
+ if (ret) {
+ media_entity_pipeline_stop(&sensor->entity);
+ return ret;
+ }
+
+ return vb2_streamon(&fimc->vb_queue, type);
+}
+
+static int fimc_lite_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR];
+ int ret;
+
+ ret = vb2_streamoff(&fimc->vb_queue, type);
+ if (ret == 0)
+ media_entity_pipeline_stop(&sd->entity);
+ return ret;
+}
+
+static int fimc_lite_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ int ret;
+
+ reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count);
+ ret = vb2_reqbufs(&fimc->vb_queue, reqbufs);
+ if (!ret < 0)
+ fimc->reqbufs_count = reqbufs->count;
+
+ return ret;
+}
+
+static int fimc_lite_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_querybuf(&fimc->vb_queue, buf);
+}
+
+static int fimc_lite_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_qbuf(&fimc->vb_queue, buf);
+}
+
+static int fimc_lite_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK);
+}
+
+static int fimc_lite_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_create_bufs(&fimc->vb_queue, create);
+}
+
+static int fimc_lite_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+
+ return vb2_prepare_buf(&fimc->vb_queue, b);
+}
+
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int fimc_lite_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct flite_frame *f = &fimc->out_frame;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = f->f_width;
+ sel->r.height = f->f_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ sel->r = f->rect;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int fimc_lite_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct fimc_lite *fimc = video_drvdata(file);
+ struct flite_frame *f = &fimc->out_frame;
+ struct v4l2_rect rect = sel->r;
+ unsigned long flags;
+
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE)
+ return -EINVAL;
+
+ fimc_lite_try_compose(fimc, &rect);
+
+ if ((sel->flags & V4L2_SEL_FLAG_LE) &&
+ !enclosed_rectangle(&rect, &sel->r))
+ return -ERANGE;
+
+ if ((sel->flags & V4L2_SEL_FLAG_GE) &&
+ !enclosed_rectangle(&sel->r, &rect))
+ return -ERANGE;
+
+ sel->r = rect;
+ spin_lock_irqsave(&fimc->slock, flags);
+ f->rect = rect;
+ set_bit(ST_FLITE_CONFIG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = {
+ .vidioc_querycap = fimc_vidioc_querycap_capture,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane,
+ .vidioc_g_selection = fimc_lite_g_selection,
+ .vidioc_s_selection = fimc_lite_s_selection,
+ .vidioc_reqbufs = fimc_lite_reqbufs,
+ .vidioc_querybuf = fimc_lite_querybuf,
+ .vidioc_prepare_buf = fimc_lite_prepare_buf,
+ .vidioc_create_bufs = fimc_lite_create_bufs,
+ .vidioc_qbuf = fimc_lite_qbuf,
+ .vidioc_dqbuf = fimc_lite_dqbuf,
+ .vidioc_streamon = fimc_lite_streamon,
+ .vidioc_streamoff = fimc_lite_streamoff,
+};
+
+/* Capture subdev media entity operations */
+static int fimc_lite_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ unsigned int remote_ent_type = media_entity_type(remote->entity);
+
+ if (WARN_ON(fimc == NULL))
+ return 0;
+
+ v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x",
+ __func__, local->entity->name, remote->entity->name,
+ flags, fimc->source_subdev_grp_id);
+
+ switch (local->index) {
+ case FIMC_SD_PAD_SINK:
+ if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EINVAL;
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (fimc->source_subdev_grp_id != 0)
+ return -EBUSY;
+ fimc->source_subdev_grp_id = sd->grp_id;
+ return 0;
+ }
+
+ fimc->source_subdev_grp_id = 0;
+ break;
+
+ case FIMC_SD_PAD_SOURCE:
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ fimc->out_path = FIMC_IO_NONE;
+ return 0;
+ }
+ if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV)
+ fimc->out_path = FIMC_IO_ISP;
+ else
+ fimc->out_path = FIMC_IO_DMA;
+ break;
+
+ default:
+ v4l2_err(sd, "Invalid pad index\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations fimc_lite_subdev_media_ops = {
+ .link_setup = fimc_lite_link_setup,
+};
+
+static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct fimc_fmt *fmt;
+
+ fmt = fimc_lite_find_format(NULL, NULL, code->index);
+ if (!fmt)
+ return -EINVAL;
+ code->code = fmt->mbus_code;
+ return 0;
+}
+
+static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct flite_frame *f = &fimc->out_frame;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+
+ mutex_lock(&fimc->lock);
+ mf->code = fimc->fmt->mbus_code;
+
+ if (fmt->pad == FLITE_SD_PAD_SINK) {
+ /* full camera input frame size */
+ mf->width = f->f_width;
+ mf->height = f->f_height;
+ } else {
+ /* crop size */
+ mf->width = f->rect.width;
+ mf->height = f->rect.height;
+ }
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct flite_frame *sink = &fimc->inp_frame;
+ const struct fimc_fmt *ffmt;
+
+ v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d",
+ fmt->pad, mf->code, mf->width, mf->height);
+
+ mf->colorspace = V4L2_COLORSPACE_JPEG;
+ mutex_lock(&fimc->lock);
+
+ if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) ||
+ (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) {
+ mutex_unlock(&fimc->lock);
+ return -EBUSY;
+ }
+
+ ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height,
+ &mf->code, NULL, fmt->pad);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(fh, fmt->pad);
+ *mf = fmt->format;
+ mutex_unlock(&fimc->lock);
+ return 0;
+ }
+
+ if (fmt->pad == FLITE_SD_PAD_SINK) {
+ sink->f_width = mf->width;
+ sink->f_height = mf->height;
+ fimc->fmt = ffmt;
+ /* Set sink crop rectangle */
+ sink->rect.width = mf->width;
+ sink->rect.height = mf->height;
+ sink->rect.left = 0;
+ sink->rect.top = 0;
+ /* Reset source crop rectangle */
+ fimc->out_frame.rect = sink->rect;
+ } else {
+ /* Allow changing format only on sink pad */
+ mf->code = fimc->fmt->mbus_code;
+ mf->width = sink->rect.width;
+ mf->height = sink->rect.height;
+ }
+
+ mutex_unlock(&fimc->lock);
+ return 0;
+}
+
+static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct flite_frame *f = &fimc->inp_frame;
+
+ if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL &&
+ sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) ||
+ sel->pad != FLITE_SD_PAD_SINK)
+ return -EINVAL;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad);
+ return 0;
+ }
+
+ mutex_lock(&fimc->lock);
+ if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) {
+ sel->r = f->rect;
+ } else {
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = f->f_width;
+ sel->r.height = f->f_height;
+ }
+ mutex_unlock(&fimc->lock);
+
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+ __func__, f->rect.left, f->rect.top, f->rect.width,
+ f->rect.height, f->f_width, f->f_height);
+
+ return 0;
+}
+
+static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct flite_frame *f = &fimc->inp_frame;
+ int ret = 0;
+
+ if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL ||
+ sel->pad != FLITE_SD_PAD_SINK)
+ return -EINVAL;
+
+ mutex_lock(&fimc->lock);
+ fimc_lite_try_crop(fimc, &sel->r);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r;
+ } else {
+ unsigned long flags;
+ spin_lock_irqsave(&fimc->slock, flags);
+ f->rect = sel->r;
+ /* Same crop rectangle on the source pad */
+ fimc->out_frame.rect = sel->r;
+ set_bit(ST_FLITE_CONFIG, &fimc->state);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ }
+ mutex_unlock(&fimc->lock);
+
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d",
+ __func__, f->rect.left, f->rect.top, f->rect.width,
+ f->rect.height, f->f_width, f->f_height);
+
+ return ret;
+}
+
+static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc->out_path == FIMC_IO_DMA)
+ return -ENOIOCTLCMD;
+
+ /* TODO: */
+
+ return 0;
+}
+
+static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc->out_path == FIMC_IO_DMA)
+ return -ENOIOCTLCMD;
+
+ /* TODO: */
+
+ return 0;
+}
+
+static int fimc_lite_log_status(struct v4l2_subdev *sd)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ flite_hw_dump_regs(fimc, __func__);
+ return 0;
+}
+
+static int fimc_lite_subdev_registered(struct v4l2_subdev *sd)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+ struct vb2_queue *q = &fimc->vb_queue;
+ struct video_device *vfd;
+ int ret;
+
+ fimc->fmt = &fimc_lite_formats[0];
+ fimc->out_path = FIMC_IO_DMA;
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(sd->v4l2_dev, "Failed to allocate video device\n");
+ return -ENOMEM;
+ }
+
+ snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture",
+ fimc->index);
+
+ vfd->fops = &fimc_lite_fops;
+ vfd->ioctl_ops = &fimc_lite_ioctl_ops;
+ vfd->v4l2_dev = sd->v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release;
+ vfd->lock = &fimc->lock;
+ fimc->vfd = vfd;
+ fimc->ref_count = 0;
+ fimc->reqbufs_count = 0;
+
+ INIT_LIST_HEAD(&fimc->pending_buf_q);
+ INIT_LIST_HEAD(&fimc->active_buf_q);
+
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->ops = &fimc_lite_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct flite_buffer);
+ q->drv_priv = fimc;
+
+ vb2_queue_init(q);
+
+ fimc->vd_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0);
+ if (ret)
+ goto err;
+
+ video_set_drvdata(vfd, fimc);
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto err_vd;
+
+ v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+ return 0;
+
+ err_vd:
+ media_entity_cleanup(&vfd->entity);
+ err:
+ video_device_release(vfd);
+ return ret;
+}
+
+static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd)
+{
+ struct fimc_lite *fimc = v4l2_get_subdevdata(sd);
+
+ if (fimc == NULL)
+ return;
+
+ if (fimc->vfd) {
+ video_unregister_device(fimc->vfd);
+ media_entity_cleanup(&fimc->vfd->entity);
+ fimc->vfd = NULL;
+ }
+}
+
+static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = {
+ .registered = fimc_lite_subdev_registered,
+ .unregistered = fimc_lite_subdev_unregistered,
+};
+
+static const struct v4l2_subdev_pad_ops fimc_lite_subdev_pad_ops = {
+ .enum_mbus_code = fimc_lite_subdev_enum_mbus_code,
+ .get_selection = fimc_lite_subdev_get_selection,
+ .set_selection = fimc_lite_subdev_set_selection,
+ .get_fmt = fimc_lite_subdev_get_fmt,
+ .set_fmt = fimc_lite_subdev_set_fmt,
+};
+
+static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = {
+ .s_stream = fimc_lite_subdev_s_stream,
+};
+
+static const struct v4l2_subdev_core_ops fimc_lite_core_ops = {
+ .s_power = fimc_lite_subdev_s_power,
+ .log_status = fimc_lite_log_status,
+};
+
+static struct v4l2_subdev_ops fimc_lite_subdev_ops = {
+ .core = &fimc_lite_core_ops,
+ .video = &fimc_lite_subdev_video_ops,
+ .pad = &fimc_lite_subdev_pad_ops,
+};
+
+static int fimc_lite_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct fimc_lite *fimc = container_of(ctrl->handler, struct fimc_lite,
+ ctrl_handler);
+ set_bit(ST_FLITE_CONFIG, &fimc->state);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops fimc_lite_ctrl_ops = {
+ .s_ctrl = fimc_lite_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config fimc_lite_ctrl = {
+ .ops = &fimc_lite_ctrl_ops,
+ .id = V4L2_CTRL_CLASS_USER | 0x1001,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Test Pattern 640x480",
+};
+
+static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc)
+{
+ struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler;
+ struct v4l2_subdev *sd = &fimc->subdev;
+ int ret;
+
+ v4l2_subdev_init(sd, &fimc_lite_subdev_ops);
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index);
+
+ fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM,
+ fimc->subdev_pads, 0);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(handler, 1);
+ fimc->test_pattern = v4l2_ctrl_new_custom(handler, &fimc_lite_ctrl,
+ NULL);
+ if (handler->error) {
+ media_entity_cleanup(&sd->entity);
+ return handler->error;
+ }
+
+ sd->ctrl_handler = handler;
+ sd->internal_ops = &fimc_lite_subdev_internal_ops;
+ sd->entity.ops = &fimc_lite_subdev_media_ops;
+ v4l2_set_subdevdata(sd, fimc);
+
+ return 0;
+}
+
+static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc)
+{
+ struct v4l2_subdev *sd = &fimc->subdev;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&fimc->ctrl_handler);
+ v4l2_set_subdevdata(sd, NULL);
+}
+
+static void fimc_lite_clk_put(struct fimc_lite *fimc)
+{
+ if (IS_ERR_OR_NULL(fimc->clock))
+ return;
+
+ clk_unprepare(fimc->clock);
+ clk_put(fimc->clock);
+ fimc->clock = NULL;
+}
+
+static int fimc_lite_clk_get(struct fimc_lite *fimc)
+{
+ int ret;
+
+ fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME);
+ if (IS_ERR(fimc->clock))
+ return PTR_ERR(fimc->clock);
+
+ ret = clk_prepare(fimc->clock);
+ if (ret < 0) {
+ clk_put(fimc->clock);
+ fimc->clock = NULL;
+ }
+ return ret;
+}
+
+static int __devinit fimc_lite_probe(struct platform_device *pdev)
+{
+ struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev);
+ struct fimc_lite *fimc;
+ struct resource *res;
+ int ret;
+
+ fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
+ if (!fimc)
+ return -ENOMEM;
+
+ fimc->index = pdev->id;
+ fimc->variant = drv_data->variant[fimc->index];
+ fimc->pdev = pdev;
+
+ init_waitqueue_head(&fimc->irq_queue);
+ spin_lock_init(&fimc->slock);
+ mutex_init(&fimc->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (fimc->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+ return -ENXIO;
+ }
+
+ ret = fimc_lite_clk_get(fimc);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler,
+ 0, dev_name(&pdev->dev), fimc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
+ goto err_clk;
+ }
+
+ /* The video node will be created within the subdev's registered() op */
+ ret = fimc_lite_create_capture_subdev(fimc);
+ if (ret)
+ goto err_clk;
+
+ platform_set_drvdata(pdev, fimc);
+ pm_runtime_enable(&pdev->dev);
+ ret = pm_runtime_get_sync(&pdev->dev);
+ if (ret < 0)
+ goto err_sd;
+
+ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(fimc->alloc_ctx)) {
+ ret = PTR_ERR(fimc->alloc_ctx);
+ goto err_pm;
+ }
+ pm_runtime_put(&pdev->dev);
+
+ dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n",
+ fimc->index);
+ return 0;
+err_pm:
+ pm_runtime_put(&pdev->dev);
+err_sd:
+ fimc_lite_unregister_capture_subdev(fimc);
+err_clk:
+ fimc_lite_clk_put(fimc);
+ return ret;
+}
+
+static int fimc_lite_runtime_resume(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+
+ clk_enable(fimc->clock);
+ return 0;
+}
+
+static int fimc_lite_runtime_suspend(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+
+ clk_disable(fimc->clock);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_lite_resume(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+ struct flite_buffer *buf;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ if (!test_and_clear_bit(ST_LPM, &fimc->state) ||
+ !test_bit(ST_FLITE_IN_USE, &fimc->state)) {
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return 0;
+ }
+ flite_hw_reset(fimc);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+
+ if (!test_and_clear_bit(ST_FLITE_SUSPENDED, &fimc->state))
+ return 0;
+
+ INIT_LIST_HEAD(&fimc->active_buf_q);
+ fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity, false);
+ fimc_lite_hw_init(fimc);
+ clear_bit(ST_FLITE_SUSPENDED, &fimc->state);
+
+ for (i = 0; i < fimc->reqbufs_count; i++) {
+ if (list_empty(&fimc->pending_buf_q))
+ break;
+ buf = fimc_lite_pending_queue_pop(fimc);
+ buffer_queue(&buf->vb);
+ }
+ return 0;
+}
+
+static int fimc_lite_suspend(struct device *dev)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+ bool suspend = test_bit(ST_FLITE_IN_USE, &fimc->state);
+ int ret;
+
+ if (test_and_set_bit(ST_LPM, &fimc->state))
+ return 0;
+
+ ret = fimc_lite_stop_capture(fimc, suspend);
+ if (ret)
+ return ret;
+
+ return fimc_pipeline_shutdown(&fimc->pipeline);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int __devexit fimc_lite_remove(struct platform_device *pdev)
+{
+ struct fimc_lite *fimc = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ fimc_lite_unregister_capture_subdev(fimc);
+ vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx);
+ fimc_lite_clk_put(fimc);
+
+ dev_info(dev, "Driver unloaded\n");
+ return 0;
+}
+
+static struct flite_variant fimc_lite0_variant_exynos4 = {
+ .max_width = 8192,
+ .max_height = 8192,
+ .out_width_align = 8,
+ .win_hor_offs_align = 2,
+ .out_hor_offs_align = 8,
+};
+
+/* EXYNOS4212, EXYNOS4412 */
+static struct flite_drvdata fimc_lite_drvdata_exynos4 = {
+ .variant = {
+ [0] = &fimc_lite0_variant_exynos4,
+ [1] = &fimc_lite0_variant_exynos4,
+ },
+};
+
+static struct platform_device_id fimc_lite_driver_ids[] = {
+ {
+ .name = "exynos-fimc-lite",
+ .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids);
+
+static const struct dev_pm_ops fimc_lite_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume)
+ SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume,
+ NULL)
+};
+
+static struct platform_driver fimc_lite_driver = {
+ .probe = fimc_lite_probe,
+ .remove = __devexit_p(fimc_lite_remove),
+ .id_table = fimc_lite_driver_ids,
+ .driver = {
+ .name = FIMC_LITE_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &fimc_lite_pm_ops,
+ }
+};
+module_platform_driver(fimc_lite_driver);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME);
diff --git a/drivers/media/video/s5p-fimc/fimc-lite.h b/drivers/media/video/s5p-fimc/fimc-lite.h
new file mode 100644
index 00000000000..44424eee81d
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-lite.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_LITE_H_
+#define FIMC_LITE_H_
+
+#include <asm/sizes.h>
+#include <linux/io.h>
+#include <linux/irqreturn.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/s5p_fimc.h>
+
+#include "fimc-core.h"
+
+#define FIMC_LITE_DRV_NAME "exynos-fimc-lite"
+#define FLITE_CLK_NAME "flite"
+#define FIMC_LITE_MAX_DEVS 2
+#define FLITE_REQ_BUFS_MIN 2
+
+/* Bit index definitions for struct fimc_lite::state */
+enum {
+ ST_FLITE_LPM,
+ ST_FLITE_PENDING,
+ ST_FLITE_RUN,
+ ST_FLITE_STREAM,
+ ST_FLITE_SUSPENDED,
+ ST_FLITE_OFF,
+ ST_FLITE_IN_USE,
+ ST_FLITE_CONFIG,
+ ST_SENSOR_STREAM,
+};
+
+#define FLITE_SD_PAD_SINK 0
+#define FLITE_SD_PAD_SOURCE 1
+#define FLITE_SD_PADS_NUM 2
+
+struct flite_variant {
+ unsigned short max_width;
+ unsigned short max_height;
+ unsigned short out_width_align;
+ unsigned short win_hor_offs_align;
+ unsigned short out_hor_offs_align;
+};
+
+struct flite_drvdata {
+ struct flite_variant *variant[FIMC_LITE_MAX_DEVS];
+};
+
+#define fimc_lite_get_drvdata(_pdev) \
+ ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data)
+
+struct fimc_lite_events {
+ unsigned int data_overflow;
+};
+
+#define FLITE_MAX_PLANES 1
+
+/**
+ * struct flite_frame - source/target frame properties
+ * @f_width: full pixel width
+ * @f_height: full pixel height
+ * @rect: crop/composition rectangle
+ */
+struct flite_frame {
+ u16 f_width;
+ u16 f_height;
+ struct v4l2_rect rect;
+};
+
+/**
+ * struct flite_buffer - video buffer structure
+ * @vb: vb2 buffer
+ * @list: list head for the buffers queue
+ * @paddr: precalculated physical address
+ */
+struct flite_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+ dma_addr_t paddr;
+};
+
+/**
+ * struct fimc_lite - fimc lite structure
+ * @pdev: pointer to FIMC-LITE platform device
+ * @variant: variant information for this IP
+ * @v4l2_dev: pointer to top the level v4l2_device
+ * @vfd: video device node
+ * @fh: v4l2 file handle
+ * @alloc_ctx: videobuf2 memory allocator context
+ * @subdev: FIMC-LITE subdev
+ * @vd_pad: media (sink) pad for the capture video node
+ * @subdev_pads: the subdev media pads
+ * @ctrl_handler: v4l2 control handler
+ * @test_pattern: test pattern controls
+ * @index: FIMC-LITE platform device index
+ * @pipeline: video capture pipeline data structure
+ * @slock: spinlock protecting this data structure and the hw registers
+ * @lock: mutex serializing video device and the subdev operations
+ * @clock: FIMC-LITE gate clock
+ * @regs: memory mapped io registers
+ * @irq_queue: interrupt handler waitqueue
+ * @fmt: pointer to color format description structure
+ * @payload: image size in bytes (w x h x bpp)
+ * @inp_frame: camera input frame structure
+ * @out_frame: DMA output frame structure
+ * @out_path: output data path (DMA or FIFO)
+ * @source_subdev_grp_id: source subdev group id
+ * @state: driver state flags
+ * @pending_buf_q: pending buffers queue head
+ * @active_buf_q: the queue head of buffers scheduled in hardware
+ * @vb_queue: vb2 buffers queue
+ * @active_buf_count: number of video buffers scheduled in hardware
+ * @frame_count: the captured frames counter
+ * @reqbufs_count: the number of buffers requested with REQBUFS ioctl
+ * @ref_count: driver's private reference counter
+ */
+struct fimc_lite {
+ struct platform_device *pdev;
+ struct flite_variant *variant;
+ struct v4l2_device *v4l2_dev;
+ struct video_device *vfd;
+ struct v4l2_fh fh;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct v4l2_subdev subdev;
+ struct media_pad vd_pad;
+ struct media_pad subdev_pads[FLITE_SD_PADS_NUM];
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *test_pattern;
+ u32 index;
+ struct fimc_pipeline pipeline;
+
+ struct mutex lock;
+ spinlock_t slock;
+
+ struct clk *clock;
+ void __iomem *regs;
+ wait_queue_head_t irq_queue;
+
+ const struct fimc_fmt *fmt;
+ unsigned long payload[FLITE_MAX_PLANES];
+ struct flite_frame inp_frame;
+ struct flite_frame out_frame;
+ enum fimc_datapath out_path;
+ unsigned int source_subdev_grp_id;
+
+ unsigned long state;
+ struct list_head pending_buf_q;
+ struct list_head active_buf_q;
+ struct vb2_queue vb_queue;
+ unsigned int frame_count;
+ unsigned int reqbufs_count;
+ int ref_count;
+
+ struct fimc_lite_events events;
+};
+
+static inline bool fimc_lite_active(struct fimc_lite *fimc)
+{
+ unsigned long flags;
+ bool ret;
+
+ spin_lock_irqsave(&fimc->slock, flags);
+ ret = fimc->state & (1 << ST_FLITE_RUN) ||
+ fimc->state & (1 << ST_FLITE_PENDING);
+ spin_unlock_irqrestore(&fimc->slock, flags);
+ return ret;
+}
+
+static inline void fimc_lite_active_queue_add(struct fimc_lite *dev,
+ struct flite_buffer *buf)
+{
+ list_add_tail(&buf->list, &dev->active_buf_q);
+}
+
+static inline struct flite_buffer *fimc_lite_active_queue_pop(
+ struct fimc_lite *dev)
+{
+ struct flite_buffer *buf = list_entry(dev->active_buf_q.next,
+ struct flite_buffer, list);
+ list_del(&buf->list);
+ return buf;
+}
+
+static inline void fimc_lite_pending_queue_add(struct fimc_lite *dev,
+ struct flite_buffer *buf)
+{
+ list_add_tail(&buf->list, &dev->pending_buf_q);
+}
+
+static inline struct flite_buffer *fimc_lite_pending_queue_pop(
+ struct fimc_lite *dev)
+{
+ struct flite_buffer *buf = list_entry(dev->pending_buf_q.next,
+ struct flite_buffer, list);
+ list_del(&buf->list);
+ return buf;
+}
+
+#endif /* FIMC_LITE_H_ */
diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c
new file mode 100644
index 00000000000..4c58e057096
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-m2m.c
@@ -0,0 +1,824 @@
+/*
+ * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, either version 2 of the License,
+ * or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "fimc-core.h"
+#include "fimc-reg.h"
+#include "fimc-mdevice.h"
+
+
+static unsigned int get_m2m_fmt_flags(unsigned int stream_type)
+{
+ if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return FMT_FLAGS_M2M_IN;
+ else
+ return FMT_FLAGS_M2M_OUT;
+}
+
+void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state)
+{
+ struct vb2_buffer *src_vb, *dst_vb;
+
+ if (!ctx || !ctx->m2m_ctx)
+ return;
+
+ src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+
+ if (src_vb && dst_vb) {
+ v4l2_m2m_buf_done(src_vb, vb_state);
+ v4l2_m2m_buf_done(dst_vb, vb_state);
+ v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev,
+ ctx->m2m_ctx);
+ }
+}
+
+/* Complete the transaction which has been scheduled for execution. */
+static int fimc_m2m_shutdown(struct fimc_ctx *ctx)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ int ret;
+
+ if (!fimc_m2m_pending(fimc))
+ return 0;
+
+ fimc_ctx_state_set(FIMC_CTX_SHUT, ctx);
+
+ ret = wait_event_timeout(fimc->irq_queue,
+ !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx),
+ FIMC_SHUTDOWN_TIMEOUT);
+
+ return ret == 0 ? -ETIMEDOUT : ret;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev);
+ return ret > 0 ? 0 : ret;
+}
+
+static int stop_streaming(struct vb2_queue *q)
+{
+ struct fimc_ctx *ctx = q->drv_priv;
+ int ret;
+
+ ret = fimc_m2m_shutdown(ctx);
+ if (ret == -ETIMEDOUT)
+ fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR);
+
+ pm_runtime_put(&ctx->fimc_dev->pdev->dev);
+ return 0;
+}
+
+static void fimc_device_run(void *priv)
+{
+ struct vb2_buffer *vb = NULL;
+ struct fimc_ctx *ctx = priv;
+ struct fimc_frame *sf, *df;
+ struct fimc_dev *fimc;
+ unsigned long flags;
+ u32 ret;
+
+ if (WARN(!ctx, "Null context\n"))
+ return;
+
+ fimc = ctx->fimc_dev;
+ spin_lock_irqsave(&fimc->slock, flags);
+
+ set_bit(ST_M2M_PEND, &fimc->state);
+ sf = &ctx->s_frame;
+ df = &ctx->d_frame;
+
+ if (ctx->state & FIMC_PARAMS) {
+ /* Prepare the DMA offsets for scaler */
+ fimc_prepare_dma_offset(ctx, sf);
+ fimc_prepare_dma_offset(ctx, df);
+ }
+
+ vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr);
+ if (ret)
+ goto dma_unlock;
+
+ vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ ret = fimc_prepare_addr(ctx, vb, df, &df->paddr);
+ if (ret)
+ goto dma_unlock;
+
+ /* Reconfigure hardware if the context has changed. */
+ if (fimc->m2m.ctx != ctx) {
+ ctx->state |= FIMC_PARAMS;
+ fimc->m2m.ctx = ctx;
+ }
+
+ if (ctx->state & FIMC_PARAMS) {
+ fimc_set_yuv_order(ctx);
+ fimc_hw_set_input_path(ctx);
+ fimc_hw_set_in_dma(ctx);
+ ret = fimc_set_scaler_info(ctx);
+ if (ret)
+ goto dma_unlock;
+ fimc_hw_set_prescaler(ctx);
+ fimc_hw_set_mainscaler(ctx);
+ fimc_hw_set_target_format(ctx);
+ fimc_hw_set_rotation(ctx);
+ fimc_hw_set_effect(ctx);
+ fimc_hw_set_out_dma(ctx);
+ if (fimc->variant->has_alpha)
+ fimc_hw_set_rgb_alpha(ctx);
+ fimc_hw_set_output_path(ctx);
+ }
+ fimc_hw_set_input_addr(fimc, &sf->paddr);
+ fimc_hw_set_output_addr(fimc, &df->paddr, -1);
+
+ fimc_activate_capture(ctx);
+ ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP |
+ FIMC_SRC_FMT | FIMC_DST_FMT);
+ fimc_hw_activate_input_dma(fimc, true);
+
+dma_unlock:
+ spin_unlock_irqrestore(&fimc->slock, flags);
+}
+
+static void fimc_job_abort(void *priv)
+{
+ fimc_m2m_shutdown(priv);
+}
+
+static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], void *allocators[])
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ struct fimc_frame *f;
+ int i;
+
+ f = ctx_get_frame(ctx, vq->type);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+ /*
+ * Return number of non-contigous planes (plane buffers)
+ * depending on the configured color format.
+ */
+ if (!f->fmt)
+ return -EINVAL;
+
+ *num_planes = f->fmt->memplanes;
+ for (i = 0; i < f->fmt->memplanes; i++) {
+ sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8;
+ allocators[i] = ctx->fimc_dev->alloc_ctx;
+ }
+ return 0;
+}
+
+static int fimc_buf_prepare(struct vb2_buffer *vb)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct fimc_frame *frame;
+ int i;
+
+ frame = ctx_get_frame(ctx, vb->vb2_queue->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ for (i = 0; i < frame->fmt->memplanes; i++)
+ vb2_set_plane_payload(vb, i, frame->payload[i]);
+
+ return 0;
+}
+
+static void fimc_buf_queue(struct vb2_buffer *vb)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state);
+
+ if (ctx->m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static void fimc_lock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_lock(&ctx->fimc_dev->lock);
+}
+
+static void fimc_unlock(struct vb2_queue *vq)
+{
+ struct fimc_ctx *ctx = vb2_get_drv_priv(vq);
+ mutex_unlock(&ctx->fimc_dev->lock);
+}
+
+static struct vb2_ops fimc_qops = {
+ .queue_setup = fimc_queue_setup,
+ .buf_prepare = fimc_buf_prepare,
+ .buf_queue = fimc_buf_queue,
+ .wait_prepare = fimc_unlock,
+ .wait_finish = fimc_lock,
+ .stop_streaming = stop_streaming,
+ .start_streaming = start_streaming,
+};
+
+/*
+ * V4L2 ioctl handlers
+ */
+static int fimc_m2m_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1);
+ strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1);
+ cap->bus_info[0] = 0;
+ cap->capabilities = V4L2_CAP_STREAMING |
+ V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ return 0;
+}
+
+static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct fimc_fmt *fmt;
+
+ fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type),
+ f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ strncpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame = ctx_get_frame(ctx, f->type);
+
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ return fimc_fill_format(frame, f);
+}
+
+static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_variant *variant = fimc->variant;
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ struct fimc_fmt *fmt;
+ u32 max_w, mod_x, mod_y;
+
+ if (!IS_M2M(f->type))
+ return -EINVAL;
+
+ dbg("w: %d, h: %d", pix->width, pix->height);
+
+ fmt = fimc_find_format(&pix->pixelformat, NULL,
+ get_m2m_fmt_flags(f->type), 0);
+ if (WARN(fmt == NULL, "Pixel format lookup failed"))
+ return -EINVAL;
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+ else if (pix->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ max_w = variant->pix_limit->scaler_dis_w;
+ mod_x = ffs(variant->min_inp_pixsize) - 1;
+ } else {
+ max_w = variant->pix_limit->out_rot_dis_w;
+ mod_x = ffs(variant->min_out_pixsize) - 1;
+ }
+
+ if (tiled_fmt(fmt)) {
+ mod_x = 6; /* 64 x 32 pixels tile */
+ mod_y = 5;
+ } else {
+ if (variant->min_vsize_align == 1)
+ mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1;
+ else
+ mod_y = ffs(variant->min_vsize_align) - 1;
+ }
+
+ v4l_bound_align_image(&pix->width, 16, max_w, mod_x,
+ &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0);
+
+ fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp);
+ return 0;
+}
+
+static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return fimc_try_fmt_mplane(ctx, f);
+}
+
+static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct vb2_queue *vq;
+ struct fimc_frame *frame;
+ struct v4l2_pix_format_mplane *pix;
+ int i, ret = 0;
+
+ ret = fimc_try_fmt_mplane(ctx, f);
+ if (ret)
+ return ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type);
+ return -EBUSY;
+ }
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ frame = &ctx->s_frame;
+ else
+ frame = &ctx->d_frame;
+
+ pix = &f->fmt.pix_mp;
+ frame->fmt = fimc_find_format(&pix->pixelformat, NULL,
+ get_m2m_fmt_flags(f->type), 0);
+ if (!frame->fmt)
+ return -EINVAL;
+
+ /* Update RGB Alpha control state and value range */
+ fimc_alpha_ctrl_update(ctx);
+
+ for (i = 0; i < frame->fmt->colplanes; i++) {
+ frame->payload[i] =
+ (pix->width * pix->height * frame->fmt->depth[i]) / 8;
+ }
+
+ fimc_fill_frame(frame, f);
+
+ ctx->scaler.enabled = 1;
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx);
+ else
+ fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx);
+
+ dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height);
+
+ return 0;
+}
+
+static int fimc_m2m_reqbufs(struct file *file, void *fh,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int fimc_m2m_querybuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_qbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_dqbuf(struct file *file, void *fh,
+ struct v4l2_buffer *buf)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int fimc_m2m_streamon(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ /* The source and target color format need to be set */
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx))
+ return -EINVAL;
+ } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) {
+ return -EINVAL;
+ }
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_streamoff(struct file *file, void *fh,
+ enum v4l2_buf_type type)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static int fimc_m2m_cropcap(struct file *file, void *fh,
+ struct v4l2_cropcap *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->bounds.left = 0;
+ cr->bounds.top = 0;
+ cr->bounds.width = frame->o_width;
+ cr->bounds.height = frame->o_height;
+ cr->defrect = cr->bounds;
+
+ return 0;
+}
+
+static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_frame *frame;
+
+ frame = ctx_get_frame(ctx, cr->type);
+ if (IS_ERR(frame))
+ return PTR_ERR(frame);
+
+ cr->c.left = frame->offs_h;
+ cr->c.top = frame->offs_v;
+ cr->c.width = frame->width;
+ cr->c.height = frame->height;
+
+ return 0;
+}
+
+static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr)
+{
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_frame *f;
+ u32 min_size, halign, depth = 0;
+ int i;
+
+ if (cr->c.top < 0 || cr->c.left < 0) {
+ v4l2_err(fimc->m2m.vfd,
+ "doesn't support negative values for top & left\n");
+ return -EINVAL;
+ }
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ f = &ctx->d_frame;
+ else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ f = &ctx->s_frame;
+ else
+ return -EINVAL;
+
+ min_size = (f == &ctx->s_frame) ?
+ fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize;
+
+ /* Get pixel alignment constraints. */
+ if (fimc->variant->min_vsize_align == 1)
+ halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1;
+ else
+ halign = ffs(fimc->variant->min_vsize_align) - 1;
+
+ for (i = 0; i < f->fmt->colplanes; i++)
+ depth += f->fmt->depth[i];
+
+ v4l_bound_align_image(&cr->c.width, min_size, f->o_width,
+ ffs(min_size) - 1,
+ &cr->c.height, min_size, f->o_height,
+ halign, 64/(ALIGN(depth, 8)));
+
+ /* adjust left/top if cropping rectangle is out of bounds */
+ if (cr->c.left + cr->c.width > f->o_width)
+ cr->c.left = f->o_width - cr->c.width;
+ if (cr->c.top + cr->c.height > f->o_height)
+ cr->c.top = f->o_height - cr->c.height;
+
+ cr->c.left = round_down(cr->c.left, min_size);
+ cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align);
+
+ dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d",
+ cr->c.left, cr->c.top, cr->c.width, cr->c.height,
+ f->f_width, f->f_height);
+
+ return 0;
+}
+
+static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(fh);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+ struct fimc_frame *f;
+ int ret;
+
+ ret = fimc_m2m_try_crop(ctx, cr);
+ if (ret)
+ return ret;
+
+ f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ?
+ &ctx->s_frame : &ctx->d_frame;
+
+ /* Check to see if scaling ratio is within supported range */
+ if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) {
+ if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ ret = fimc_check_scaler_ratio(ctx, cr->c.width,
+ cr->c.height, ctx->d_frame.width,
+ ctx->d_frame.height, ctx->rotation);
+ } else {
+ ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width,
+ ctx->s_frame.height, cr->c.width,
+ cr->c.height, ctx->rotation);
+ }
+ if (ret) {
+ v4l2_err(fimc->m2m.vfd, "Out of scaler range\n");
+ return -EINVAL;
+ }
+ }
+
+ f->offs_h = cr->c.left;
+ f->offs_v = cr->c.top;
+ f->width = cr->c.width;
+ f->height = cr->c.height;
+
+ fimc_ctx_state_set(FIMC_PARAMS, ctx);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = {
+ .vidioc_querycap = fimc_m2m_querycap,
+ .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane,
+ .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane,
+ .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane,
+ .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane,
+ .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane,
+ .vidioc_reqbufs = fimc_m2m_reqbufs,
+ .vidioc_querybuf = fimc_m2m_querybuf,
+ .vidioc_qbuf = fimc_m2m_qbuf,
+ .vidioc_dqbuf = fimc_m2m_dqbuf,
+ .vidioc_streamon = fimc_m2m_streamon,
+ .vidioc_streamoff = fimc_m2m_streamoff,
+ .vidioc_g_crop = fimc_m2m_g_crop,
+ .vidioc_s_crop = fimc_m2m_s_crop,
+ .vidioc_cropcap = fimc_m2m_cropcap
+
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct fimc_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ src_vq->drv_priv = ctx;
+ src_vq->ops = &fimc_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ dst_vq->drv_priv = ctx;
+ dst_vq->ops = &fimc_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+
+ return vb2_queue_init(dst_vq);
+}
+
+static int fimc_m2m_open(struct file *file)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
+ struct fimc_ctx *ctx;
+ int ret;
+
+ dbg("pid: %d, state: 0x%lx, refcnt: %d",
+ task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt);
+
+ /*
+ * Return if the corresponding video capture node
+ * is already opened.
+ */
+ if (fimc->vid_cap.refcnt > 0)
+ return -EBUSY;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+ v4l2_fh_init(&ctx->fh, fimc->m2m.vfd);
+ ctx->fimc_dev = fimc;
+
+ /* Default color format */
+ ctx->s_frame.fmt = fimc_get_format(0);
+ ctx->d_frame.fmt = fimc_get_format(0);
+
+ ret = fimc_ctrls_create(ctx);
+ if (ret)
+ goto error_fh;
+
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrls.handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ /* Setup the device context for memory-to-memory mode */
+ ctx->state = FIMC_CTX_M2M;
+ ctx->flags = 0;
+ ctx->in_path = FIMC_IO_DMA;
+ ctx->out_path = FIMC_IO_DMA;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->m2m_ctx)) {
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error_c;
+ }
+
+ if (fimc->m2m.refcnt++ == 0)
+ set_bit(ST_M2M_RUN, &fimc->state);
+ return 0;
+
+error_c:
+ fimc_ctrls_delete(ctx);
+error_fh:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
+}
+
+static int fimc_m2m_release(struct file *file)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+ struct fimc_dev *fimc = ctx->fimc_dev;
+
+ dbg("pid: %d, state: 0x%lx, refcnt= %d",
+ task_pid_nr(current), fimc->state, fimc->m2m.refcnt);
+
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ fimc_ctrls_delete(ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+
+ if (--fimc->m2m.refcnt <= 0)
+ clear_bit(ST_M2M_RUN, &fimc->state);
+ kfree(ctx);
+ return 0;
+}
+
+static unsigned int fimc_m2m_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+
+static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fimc_ctx *ctx = fh_to_ctx(file->private_data);
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations fimc_m2m_fops = {
+ .owner = THIS_MODULE,
+ .open = fimc_m2m_open,
+ .release = fimc_m2m_release,
+ .poll = fimc_m2m_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = fimc_m2m_mmap,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = fimc_device_run,
+ .job_abort = fimc_job_abort,
+};
+
+int fimc_register_m2m_device(struct fimc_dev *fimc,
+ struct v4l2_device *v4l2_dev)
+{
+ struct video_device *vfd;
+ struct platform_device *pdev;
+ int ret = 0;
+
+ if (!fimc)
+ return -ENODEV;
+
+ pdev = fimc->pdev;
+ fimc->v4l2_dev = v4l2_dev;
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(v4l2_dev, "Failed to allocate video device\n");
+ return -ENOMEM;
+ }
+
+ vfd->fops = &fimc_m2m_fops;
+ vfd->ioctl_ops = &fimc_m2m_ioctl_ops;
+ vfd->v4l2_dev = v4l2_dev;
+ vfd->minor = -1;
+ vfd->release = video_device_release;
+ vfd->lock = &fimc->lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
+
+ snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id);
+ video_set_drvdata(vfd, fimc);
+
+ fimc->m2m.vfd = vfd;
+ fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(fimc->m2m.m2m_dev)) {
+ v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n");
+ ret = PTR_ERR(fimc->m2m.m2m_dev);
+ goto err_init;
+ }
+
+ ret = media_entity_init(&vfd->entity, 0, NULL, 0);
+ if (ret)
+ goto err_me;
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto err_vd;
+
+ v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
+ vfd->name, video_device_node_name(vfd));
+ return 0;
+
+err_vd:
+ media_entity_cleanup(&vfd->entity);
+err_me:
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+err_init:
+ video_device_release(fimc->m2m.vfd);
+ return ret;
+}
+
+void fimc_unregister_m2m_device(struct fimc_dev *fimc)
+{
+ if (!fimc)
+ return;
+
+ if (fimc->m2m.m2m_dev)
+ v4l2_m2m_release(fimc->m2m.m2m_dev);
+ if (fimc->m2m.vfd) {
+ media_entity_cleanup(&fimc->m2m.vfd->entity);
+ /* Can also be called if video device wasn't registered */
+ video_unregister_device(fimc->m2m.vfd);
+ }
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
index 62ed37e4014..6753c45631b 100644
--- a/drivers/media/video/s5p-fimc/fimc-mdevice.c
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c
@@ -25,6 +25,7 @@
#include <media/media-device.h>
#include "fimc-core.h"
+#include "fimc-lite.h"
#include "fimc-mdevice.h"
#include "mipi-csis.h"
@@ -37,22 +38,46 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
*
* Caller holds the graph mutex.
*/
-void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me)
+void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me)
{
- struct media_entity_graph graph;
+ struct media_pad *pad = &me->pads[0];
struct v4l2_subdev *sd;
+ int i;
- media_entity_graph_walk_start(&graph, me);
+ for (i = 0; i < IDX_MAX; i++)
+ p->subdevs[i] = NULL;
- while ((me = media_entity_graph_walk_next(&graph))) {
- if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV)
- continue;
- sd = media_entity_to_v4l2_subdev(me);
+ while (1) {
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ /* source pad */
+ pad = media_entity_remote_source(pad);
+ if (pad == NULL ||
+ media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
- if (sd->grp_id == SENSOR_GROUP_ID)
- fimc->pipeline.sensor = sd;
- else if (sd->grp_id == CSIS_GROUP_ID)
- fimc->pipeline.csis = sd;
+ switch (sd->grp_id) {
+ case SENSOR_GROUP_ID:
+ p->subdevs[IDX_SENSOR] = sd;
+ break;
+ case CSIS_GROUP_ID:
+ p->subdevs[IDX_CSIS] = sd;
+ break;
+ case FLITE_GROUP_ID:
+ p->subdevs[IDX_FLITE] = sd;
+ break;
+ case FIMC_GROUP_ID:
+ /* No need to control FIMC subdev through subdev ops */
+ break;
+ default:
+ pr_warn("%s: Unknown subdev grp_id: %#x\n",
+ __func__, sd->grp_id);
+ }
+ /* sink pad */
+ pad = &sd->entity.pads[0];
}
}
@@ -85,30 +110,27 @@ static int __subdev_set_power(struct v4l2_subdev *sd, int on)
/**
* fimc_pipeline_s_power - change power state of all pipeline subdevs
* @fimc: fimc device terminating the pipeline
- * @state: 1 to enable power or 0 for power down
+ * @state: true to power on, false to power off
*
- * Need to be called with the graph mutex held.
+ * Needs to be called with the graph mutex held.
*/
-int fimc_pipeline_s_power(struct fimc_dev *fimc, int state)
+int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state)
{
- int ret = 0;
+ unsigned int i;
+ int ret;
- if (fimc->pipeline.sensor == NULL)
+ if (p->subdevs[IDX_SENSOR] == NULL)
return -ENXIO;
- if (state) {
- ret = __subdev_set_power(fimc->pipeline.csis, 1);
- if (ret && ret != -ENXIO)
+ for (i = 0; i < IDX_MAX; i++) {
+ unsigned int idx = state ? (IDX_MAX - 1) - i : i;
+
+ ret = __subdev_set_power(p->subdevs[idx], state);
+ if (ret < 0 && ret != -ENXIO)
return ret;
- return __subdev_set_power(fimc->pipeline.sensor, 1);
}
- ret = __subdev_set_power(fimc->pipeline.sensor, 0);
- if (ret)
- return ret;
- ret = __subdev_set_power(fimc->pipeline.csis, 0);
-
- return ret == -ENXIO ? 0 : ret;
+ return 0;
}
/**
@@ -119,32 +141,36 @@ int fimc_pipeline_s_power(struct fimc_dev *fimc, int state)
*
* This function must be called with the graph mutex held.
*/
-static int __fimc_pipeline_initialize(struct fimc_dev *fimc,
+static int __fimc_pipeline_initialize(struct fimc_pipeline *p,
struct media_entity *me, bool prep)
{
int ret;
if (prep)
- fimc_pipeline_prepare(fimc, me);
- if (fimc->pipeline.sensor == NULL)
+ fimc_pipeline_prepare(p, me);
+
+ if (p->subdevs[IDX_SENSOR] == NULL)
return -EINVAL;
- ret = fimc_md_set_camclk(fimc->pipeline.sensor, true);
+
+ ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true);
if (ret)
return ret;
- return fimc_pipeline_s_power(fimc, 1);
+
+ return fimc_pipeline_s_power(p, 1);
}
-int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me,
bool prep)
{
int ret;
mutex_lock(&me->parent->graph_mutex);
- ret = __fimc_pipeline_initialize(fimc, me, prep);
+ ret = __fimc_pipeline_initialize(p, me, prep);
mutex_unlock(&me->parent->graph_mutex);
return ret;
}
+EXPORT_SYMBOL_GPL(fimc_pipeline_initialize);
/**
* __fimc_pipeline_shutdown - disable the sensor clock and pipeline power
@@ -154,52 +180,55 @@ int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
* sensor clock.
* Called with the graph mutex held.
*/
-int __fimc_pipeline_shutdown(struct fimc_dev *fimc)
+int __fimc_pipeline_shutdown(struct fimc_pipeline *p)
{
int ret = 0;
- if (fimc->pipeline.sensor) {
- ret = fimc_pipeline_s_power(fimc, 0);
- fimc_md_set_camclk(fimc->pipeline.sensor, false);
+ if (p->subdevs[IDX_SENSOR]) {
+ ret = fimc_pipeline_s_power(p, 0);
+ fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false);
}
return ret == -ENXIO ? 0 : ret;
}
-int fimc_pipeline_shutdown(struct fimc_dev *fimc)
+int fimc_pipeline_shutdown(struct fimc_pipeline *p)
{
- struct media_entity *me = &fimc->vid_cap.vfd->entity;
+ struct media_entity *me = &p->subdevs[IDX_SENSOR]->entity;
int ret;
mutex_lock(&me->parent->graph_mutex);
- ret = __fimc_pipeline_shutdown(fimc);
+ ret = __fimc_pipeline_shutdown(p);
mutex_unlock(&me->parent->graph_mutex);
return ret;
}
+EXPORT_SYMBOL_GPL(fimc_pipeline_shutdown);
/**
* fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs
- * @fimc: fimc device terminating the pipeline
+ * @pipeline: video pipeline structure
* @on: passed as the s_stream call argument
*/
-int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on)
+int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on)
{
- struct fimc_pipeline *p = &fimc->pipeline;
- int ret = 0;
+ int i, ret;
- if (p->sensor == NULL)
+ if (p->subdevs[IDX_SENSOR] == NULL)
return -ENODEV;
- if ((on && p->csis) || !on)
- ret = v4l2_subdev_call(on ? p->csis : p->sensor,
- video, s_stream, on);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
- if ((!on && p->csis) || on)
- ret = v4l2_subdev_call(on ? p->sensor : p->csis,
- video, s_stream, on);
- return ret == -ENOIOCTLCMD ? 0 : ret;
+ for (i = 0; i < IDX_MAX; i++) {
+ unsigned int idx = on ? (IDX_MAX - 1) - i : i;
+
+ ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+ }
+
+ return 0;
+
}
+EXPORT_SYMBOL_GPL(fimc_pipeline_s_stream);
/*
* Sensor subdevice helper functions
@@ -214,14 +243,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd,
return NULL;
adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num);
- if (!adapter)
- return NULL;
+ if (!adapter) {
+ v4l2_warn(&fmd->v4l2_dev,
+ "Failed to get I2C adapter %d, deferring probe\n",
+ s_info->pdata->i2c_bus_num);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter,
s_info->pdata->board_info, NULL);
if (IS_ERR_OR_NULL(sd)) {
i2c_put_adapter(adapter);
- v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n");
- return NULL;
+ v4l2_warn(&fmd->v4l2_dev,
+ "Failed to acquire subdev %s, deferring probe\n",
+ s_info->pdata->board_info->type);
+ return ERR_PTR(-EPROBE_DEFER);
}
v4l2_set_subdev_hostdata(sd, s_info);
sd->grp_id = SENSOR_GROUP_ID;
@@ -269,13 +304,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
fmd->num_sensors = num_clients;
for (i = 0; i < num_clients; i++) {
+ struct v4l2_subdev *sd;
+
fmd->sensor[i].pdata = &pdata->isp_info[i];
ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true);
if (ret)
break;
- fmd->sensor[i].subdev =
- fimc_md_register_sensor(fmd, &fmd->sensor[i]);
+ sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]);
ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false);
+
+ if (!IS_ERR(sd)) {
+ fmd->sensor[i].subdev = sd;
+ } else {
+ fmd->sensor[i].subdev = NULL;
+ ret = PTR_ERR(sd);
+ break;
+ }
if (ret)
break;
}
@@ -289,21 +333,50 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
static int fimc_register_callback(struct device *dev, void *p)
{
struct fimc_dev *fimc = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = &fimc->vid_cap.subdev;
struct fimc_md *fmd = p;
- int ret;
+ int ret = 0;
if (!fimc || !fimc->pdev)
return 0;
+
if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS)
return 0;
fmd->fimc[fimc->pdev->id] = fimc;
- ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev);
- if (ret)
- return ret;
- ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev);
- if (!ret)
- fimc->vid_cap.user_subdev_api = fmd->user_subdev_api;
+ sd->grp_id = FIMC_GROUP_ID;
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret) {
+ v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n",
+ fimc->id, ret);
+ }
+
+ return ret;
+}
+
+static int fimc_lite_register_callback(struct device *dev, void *p)
+{
+ struct fimc_lite *fimc = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = &fimc->subdev;
+ struct fimc_md *fmd = p;
+ int ret;
+
+ if (fimc == NULL)
+ return 0;
+
+ if (fimc->index >= FIMC_LITE_MAX_DEVS)
+ return 0;
+
+ fmd->fimc_lite[fimc->index] = fimc;
+ sd->grp_id = FLITE_GROUP_ID;
+
+ ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd);
+ if (ret) {
+ v4l2_err(&fmd->v4l2_dev,
+ "Failed to register FIMC-LITE.%d (%d)\n",
+ fimc->index, ret);
+ }
return ret;
}
@@ -336,22 +409,56 @@ static int csis_register_callback(struct device *dev, void *p)
*/
static int fimc_md_register_platform_entities(struct fimc_md *fmd)
{
+ struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data;
struct device_driver *driver;
- int ret;
+ int ret, i;
driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type);
- if (!driver)
- return -ENODEV;
+ if (!driver) {
+ v4l2_warn(&fmd->v4l2_dev,
+ "%s driver not found, deffering probe\n",
+ FIMC_MODULE_NAME);
+ return -EPROBE_DEFER;
+ }
+
ret = driver_for_each_device(driver, NULL, fmd,
fimc_register_callback);
if (ret)
return ret;
- driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
- if (driver)
+ driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type);
+ if (driver && try_module_get(driver->owner)) {
ret = driver_for_each_device(driver, NULL, fmd,
- csis_register_callback);
- return ret;
+ fimc_lite_register_callback);
+ if (ret)
+ return ret;
+ module_put(driver->owner);
+ }
+ /*
+ * Check if there is any sensor on the MIPI-CSI2 bus and
+ * if not skip the s5p-csis module loading.
+ */
+ if (pdata == NULL)
+ return 0;
+ for (i = 0; i < pdata->num_clients; i++) {
+ if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) {
+ ret = 1;
+ break;
+ }
+ }
+ if (!ret)
+ return 0;
+
+ driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type);
+ if (!driver || !try_module_get(driver->owner)) {
+ v4l2_warn(&fmd->v4l2_dev,
+ "%s driver not found, deffering probe\n",
+ CSIS_DRIVER_NAME);
+ return -EPROBE_DEFER;
+ }
+
+ return driver_for_each_device(driver, NULL, fmd,
+ csis_register_callback);
}
static void fimc_md_unregister_entities(struct fimc_md *fmd)
@@ -361,14 +468,20 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (fmd->fimc[i] == NULL)
continue;
- fimc_unregister_m2m_device(fmd->fimc[i]);
- fimc_unregister_capture_device(fmd->fimc[i]);
+ v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev);
fmd->fimc[i] = NULL;
}
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ if (fmd->fimc_lite[i] == NULL)
+ continue;
+ v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev);
+ fmd->fimc_lite[i] = NULL;
+ }
for (i = 0; i < CSIS_MAX_ENTITIES; i++) {
if (fmd->csis[i].sd == NULL)
continue;
v4l2_device_unregister_subdev(fmd->csis[i].sd);
+ module_put(fmd->csis[i].sd->owner);
fmd->csis[i].sd = NULL;
}
for (i = 0; i < fmd->num_sensors; i++) {
@@ -379,35 +492,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd)
}
}
-static int fimc_md_register_video_nodes(struct fimc_md *fmd)
-{
- struct video_device *vdev;
- int i, ret = 0;
-
- for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) {
- if (!fmd->fimc[i])
- continue;
-
- vdev = fmd->fimc[i]->m2m.vfd;
- if (vdev) {
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
- if (ret)
- break;
- v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n",
- vdev->name, video_device_node_name(vdev));
- }
-
- vdev = fmd->fimc[i]->vid_cap.vfd;
- if (vdev == NULL)
- continue;
- ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
- v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n",
- vdev->name, video_device_node_name(vdev));
- }
-
- return ret;
-}
-
/**
* __fimc_md_create_fimc_links - create links to all FIMC entities
* @fmd: fimc media device
@@ -416,29 +500,29 @@ static int fimc_md_register_video_nodes(struct fimc_md *fmd)
* @pad: the source entity pad index
* @fimc_id: index of the fimc device for which link should be enabled
*/
-static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
- struct media_entity *source,
- struct v4l2_subdev *sensor,
- int pad, int fimc_id)
+static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd,
+ struct media_entity *source,
+ struct v4l2_subdev *sensor,
+ int pad, int fimc_id)
{
struct fimc_sensor_info *s_info;
struct media_entity *sink;
- unsigned int flags;
+ unsigned int flags = 0;
int ret, i;
for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (!fmd->fimc[i])
- break;
+ continue;
/*
* Some FIMC variants are not fitted with camera capture
* interface. Skip creating a link from sensor for those.
*/
- if (sensor->grp_id == SENSOR_GROUP_ID &&
- !fmd->fimc[i]->variant->has_cam_if)
+ if (!fmd->fimc[i]->variant->has_cam_if)
continue;
flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
- sink = &fmd->fimc[i]->vid_cap.subdev->entity;
+
+ sink = &fmd->fimc[i]->vid_cap.subdev.entity;
ret = media_entity_create_link(source, pad, sink,
FIMC_SD_PAD_SINK, flags);
if (ret)
@@ -453,7 +537,7 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
source->name, flags ? '=' : '-', sink->name);
- if (flags == 0)
+ if (flags == 0 || sensor == NULL)
continue;
s_info = v4l2_get_subdev_hostdata(sensor);
if (!WARN_ON(s_info == NULL)) {
@@ -463,9 +547,55 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd,
spin_unlock_irqrestore(&fmd->slock, irq_flags);
}
}
+
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ if (!fmd->fimc_lite[i])
+ continue;
+
+ flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0;
+
+ sink = &fmd->fimc_lite[i]->subdev.entity;
+ ret = media_entity_create_link(source, pad, sink,
+ FLITE_SD_PAD_SINK, flags);
+ if (ret)
+ return ret;
+
+ /* Notify FIMC-LITE subdev entity */
+ ret = media_entity_call(sink, link_setup, &sink->pads[0],
+ &source->pads[pad], flags);
+ if (ret)
+ break;
+
+ v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]",
+ source->name, flags ? '=' : '-', sink->name);
+ }
return 0;
}
+/* Create links from FIMC-LITE source pads to other entities */
+static int __fimc_md_create_flite_source_links(struct fimc_md *fmd)
+{
+ struct media_entity *source, *sink;
+ unsigned int flags = MEDIA_LNK_FL_ENABLED;
+ int i, ret;
+
+ for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) {
+ struct fimc_lite *fimc = fmd->fimc_lite[i];
+ if (fimc == NULL)
+ continue;
+ source = &fimc->subdev.entity;
+ sink = &fimc->vfd->entity;
+ /* FIMC-LITE's subdev and video node */
+ ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
+ sink, 0, flags);
+ if (ret)
+ break;
+ /* TODO: create links to other entities */
+ }
+
+ return ret;
+}
+
/**
* fimc_md_create_links - create default links between registered entities
*
@@ -522,8 +652,7 @@ static int fimc_md_create_links(struct fimc_md *fmd)
v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]",
sensor->entity.name, csis->entity.name);
- source = &csis->entity;
- pad = CSIS_PAD_SOURCE;
+ source = NULL;
break;
case FIMC_ITU_601...FIMC_ITU_656:
@@ -539,15 +668,27 @@ static int fimc_md_create_links(struct fimc_md *fmd)
if (source == NULL)
continue;
- ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad,
- fimc_id++);
+ ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor,
+ pad, fimc_id++);
}
+
+ fimc_id = 0;
+ for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) {
+ if (fmd->csis[i].sd == NULL)
+ continue;
+ source = &fmd->csis[i].sd->entity;
+ pad = CSIS_PAD_SOURCE;
+
+ ret = __fimc_md_create_fimc_sink_links(fmd, source, NULL,
+ pad, fimc_id++);
+ }
+
/* Create immutable links between each FIMC's subdev and video node */
flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED;
for (i = 0; i < FIMC_MAX_DEVS; i++) {
if (!fmd->fimc[i])
continue;
- source = &fmd->fimc[i]->vid_cap.subdev->entity;
+ source = &fmd->fimc[i]->vid_cap.subdev.entity;
sink = &fmd->fimc[i]->vid_cap.vfd->entity;
ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE,
sink, 0, flags);
@@ -555,7 +696,7 @@ static int fimc_md_create_links(struct fimc_md *fmd)
break;
}
- return ret;
+ return __fimc_md_create_flite_source_links(fmd);
}
/*
@@ -663,24 +804,40 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on)
static int fimc_md_link_notify(struct media_pad *source,
struct media_pad *sink, u32 flags)
{
+ struct fimc_lite *fimc_lite = NULL;
+ struct fimc_dev *fimc = NULL;
+ struct fimc_pipeline *pipeline;
struct v4l2_subdev *sd;
- struct fimc_dev *fimc;
int ret = 0;
if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
return 0;
sd = media_entity_to_v4l2_subdev(sink->entity);
- fimc = v4l2_get_subdevdata(sd);
- if (!(flags & MEDIA_LNK_FL_ENABLED)) {
- ret = __fimc_pipeline_shutdown(fimc);
- fimc->pipeline.sensor = NULL;
- fimc->pipeline.csis = NULL;
+ switch (sd->grp_id) {
+ case FLITE_GROUP_ID:
+ fimc_lite = v4l2_get_subdevdata(sd);
+ pipeline = &fimc_lite->pipeline;
+ break;
+ case FIMC_GROUP_ID:
+ fimc = v4l2_get_subdevdata(sd);
+ pipeline = &fimc->pipeline;
+ break;
+ default:
+ return 0;
+ }
- mutex_lock(&fimc->lock);
- fimc_ctrls_delete(fimc->vid_cap.ctx);
- mutex_unlock(&fimc->lock);
+ if (!(flags & MEDIA_LNK_FL_ENABLED)) {
+ ret = __fimc_pipeline_shutdown(pipeline);
+ pipeline->subdevs[IDX_SENSOR] = NULL;
+ pipeline->subdevs[IDX_CSIS] = NULL;
+
+ if (fimc) {
+ mutex_lock(&fimc->lock);
+ fimc_ctrls_delete(fimc->vid_cap.ctx);
+ mutex_unlock(&fimc->lock);
+ }
return ret;
}
/*
@@ -688,14 +845,23 @@ static int fimc_md_link_notify(struct media_pad *source,
* pipeline is already in use, i.e. its video node is opened.
* Recreate the controls destroyed during the link deactivation.
*/
- mutex_lock(&fimc->lock);
- if (fimc->vid_cap.refcnt > 0) {
- ret = __fimc_pipeline_initialize(fimc, source->entity, true);
+ if (fimc) {
+ mutex_lock(&fimc->lock);
+ if (fimc->vid_cap.refcnt > 0) {
+ ret = __fimc_pipeline_initialize(pipeline,
+ source->entity, true);
if (!ret)
ret = fimc_capture_ctrls_create(fimc);
+ }
+ mutex_unlock(&fimc->lock);
+ } else {
+ mutex_lock(&fimc_lite->lock);
+ if (fimc_lite->ref_count > 0) {
+ ret = __fimc_pipeline_initialize(pipeline,
+ source->entity, true);
+ }
+ mutex_unlock(&fimc_lite->lock);
}
- mutex_unlock(&fimc->lock);
-
return ret ? -EPIPE : ret;
}
@@ -744,7 +910,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev,
static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO,
fimc_md_sysfs_show, fimc_md_sysfs_store);
-static int __devinit fimc_md_probe(struct platform_device *pdev)
+static int fimc_md_probe(struct platform_device *pdev)
{
struct v4l2_device *v4l2_dev;
struct fimc_md *fmd;
@@ -776,42 +942,48 @@ static int __devinit fimc_md_probe(struct platform_device *pdev)
ret = media_device_register(&fmd->media_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret);
- goto err2;
+ goto err_md;
}
ret = fimc_md_get_clocks(fmd);
if (ret)
- goto err3;
+ goto err_clk;
fmd->user_subdev_api = false;
+
+ /* Protect the media graph while we're registering entities */
+ mutex_lock(&fmd->media_dev.graph_mutex);
+
ret = fimc_md_register_platform_entities(fmd);
if (ret)
- goto err3;
+ goto err_unlock;
if (pdev->dev.platform_data) {
ret = fimc_md_register_sensor_entities(fmd);
if (ret)
- goto err3;
+ goto err_unlock;
}
ret = fimc_md_create_links(fmd);
if (ret)
- goto err3;
+ goto err_unlock;
ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
if (ret)
- goto err3;
- ret = fimc_md_register_video_nodes(fmd);
- if (ret)
- goto err3;
+ goto err_unlock;
ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode);
- if (!ret) {
- platform_set_drvdata(pdev, fmd);
- return 0;
- }
-err3:
+ if (ret)
+ goto err_unlock;
+
+ platform_set_drvdata(pdev, fmd);
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ return 0;
+
+err_unlock:
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+err_clk:
media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
fimc_md_unregister_entities(fmd);
-err2:
+err_md:
v4l2_device_unregister(&fmd->v4l2_dev);
return ret;
}
@@ -841,10 +1013,12 @@ static struct platform_driver fimc_md_driver = {
int __init fimc_md_init(void)
{
int ret;
+
request_module("s5p-csis");
ret = fimc_register_driver();
if (ret)
return ret;
+
return platform_driver_register(&fimc_md_driver);
}
void __exit fimc_md_exit(void)
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h
index da3780823e7..3b8a3492a17 100644
--- a/drivers/media/video/s5p-fimc/fimc-mdevice.h
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -18,12 +18,15 @@
#include <media/v4l2-subdev.h>
#include "fimc-core.h"
+#include "fimc-lite.h"
#include "mipi-csis.h"
-/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */
+/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */
#define SENSOR_GROUP_ID (1 << 8)
#define CSIS_GROUP_ID (1 << 9)
#define WRITEBACK_GROUP_ID (1 << 10)
+#define FIMC_GROUP_ID (1 << 11)
+#define FLITE_GROUP_ID (1 << 12)
#define FIMC_MAX_SENSORS 8
#define FIMC_MAX_CAMCLKS 2
@@ -73,6 +76,7 @@ struct fimc_md {
struct fimc_sensor_info sensor[FIMC_MAX_SENSORS];
int num_sensors;
struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS];
+ struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS];
struct fimc_dev *fimc[FIMC_MAX_DEVS];
struct media_device media_dev;
struct v4l2_device v4l2_dev;
@@ -108,11 +112,11 @@ static inline void fimc_md_graph_unlock(struct fimc_dev *fimc)
}
int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
-void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me);
-int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me,
+void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me);
+int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me,
bool resume);
-int fimc_pipeline_shutdown(struct fimc_dev *fimc);
-int fimc_pipeline_s_power(struct fimc_dev *fimc, int state);
-int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state);
+int fimc_pipeline_shutdown(struct fimc_pipeline *p);
+int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state);
+int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool state);
#endif
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c
index 15466d0529c..1fc4ce8446f 100644
--- a/drivers/media/video/s5p-fimc/fimc-reg.c
+++ b/drivers/media/video/s5p-fimc/fimc-reg.c
@@ -1,9 +1,8 @@
/*
* Register interface file for Samsung Camera Interface (FIMC) driver
*
- * Copyright (c) 2010 Samsung Electronics
- *
- * Sylwester Nawrocki, s.nawrocki@samsung.com
+ * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -12,9 +11,9 @@
#include <linux/io.h>
#include <linux/delay.h>
-#include <mach/map.h>
#include <media/s5p_fimc.h>
+#include "fimc-reg.h"
#include "fimc-core.h"
@@ -22,19 +21,19 @@ void fimc_hw_reset(struct fimc_dev *dev)
{
u32 cfg;
- cfg = readl(dev->regs + S5P_CISRCFMT);
- cfg |= S5P_CISRCFMT_ITU601_8BIT;
- writel(cfg, dev->regs + S5P_CISRCFMT);
+ cfg = readl(dev->regs + FIMC_REG_CISRCFMT);
+ cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT;
+ writel(cfg, dev->regs + FIMC_REG_CISRCFMT);
/* Software reset. */
- cfg = readl(dev->regs + S5P_CIGCTRL);
- cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL);
- writel(cfg, dev->regs + S5P_CIGCTRL);
+ cfg = readl(dev->regs + FIMC_REG_CIGCTRL);
+ cfg |= (FIMC_REG_CIGCTRL_SWRST | FIMC_REG_CIGCTRL_IRQ_LEVEL);
+ writel(cfg, dev->regs + FIMC_REG_CIGCTRL);
udelay(10);
- cfg = readl(dev->regs + S5P_CIGCTRL);
- cfg &= ~S5P_CIGCTRL_SWRST;
- writel(cfg, dev->regs + S5P_CIGCTRL);
+ cfg = readl(dev->regs + FIMC_REG_CIGCTRL);
+ cfg &= ~FIMC_REG_CIGCTRL_SWRST;
+ writel(cfg, dev->regs + FIMC_REG_CIGCTRL);
if (dev->variant->out_buf_count > 4)
fimc_hw_set_dma_seq(dev, 0xF);
@@ -42,32 +41,32 @@ void fimc_hw_reset(struct fimc_dev *dev)
static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx)
{
- u32 flip = S5P_MSCTRL_FLIP_NORMAL;
+ u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL;
if (ctx->hflip)
- flip = S5P_MSCTRL_FLIP_X_MIRROR;
+ flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR;
if (ctx->vflip)
- flip = S5P_MSCTRL_FLIP_Y_MIRROR;
+ flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR;
if (ctx->rotation <= 90)
return flip;
- return (flip ^ S5P_MSCTRL_FLIP_180) & S5P_MSCTRL_FLIP_180;
+ return (flip ^ FIMC_REG_MSCTRL_FLIP_180) & FIMC_REG_MSCTRL_FLIP_180;
}
static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx)
{
- u32 flip = S5P_CITRGFMT_FLIP_NORMAL;
+ u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL;
if (ctx->hflip)
- flip |= S5P_CITRGFMT_FLIP_X_MIRROR;
+ flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR;
if (ctx->vflip)
- flip |= S5P_CITRGFMT_FLIP_Y_MIRROR;
+ flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR;
if (ctx->rotation <= 90)
return flip;
- return (flip ^ S5P_CITRGFMT_FLIP_180) & S5P_CITRGFMT_FLIP_180;
+ return (flip ^ FIMC_REG_CITRGFMT_FLIP_180) & FIMC_REG_CITRGFMT_FLIP_180;
}
void fimc_hw_set_rotation(struct fimc_ctx *ctx)
@@ -75,9 +74,9 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx)
u32 cfg, flip;
struct fimc_dev *dev = ctx->fimc_dev;
- cfg = readl(dev->regs + S5P_CITRGFMT);
- cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90 |
- S5P_CITRGFMT_FLIP_180);
+ cfg = readl(dev->regs + FIMC_REG_CITRGFMT);
+ cfg &= ~(FIMC_REG_CITRGFMT_INROT90 | FIMC_REG_CITRGFMT_OUTROT90 |
+ FIMC_REG_CITRGFMT_FLIP_180);
/*
* The input and output rotator cannot work simultaneously.
@@ -85,21 +84,21 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx)
* in direct fifo output mode.
*/
if (ctx->rotation == 90 || ctx->rotation == 270) {
- if (ctx->out_path == FIMC_LCDFIFO)
- cfg |= S5P_CITRGFMT_INROT90;
+ if (ctx->out_path == FIMC_IO_LCDFIFO)
+ cfg |= FIMC_REG_CITRGFMT_INROT90;
else
- cfg |= S5P_CITRGFMT_OUTROT90;
+ cfg |= FIMC_REG_CITRGFMT_OUTROT90;
}
- if (ctx->out_path == FIMC_DMA) {
+ if (ctx->out_path == FIMC_IO_DMA) {
cfg |= fimc_hw_get_target_flip(ctx);
- writel(cfg, dev->regs + S5P_CITRGFMT);
+ writel(cfg, dev->regs + FIMC_REG_CITRGFMT);
} else {
/* LCD FIFO path */
- flip = readl(dev->regs + S5P_MSCTRL);
- flip &= ~S5P_MSCTRL_FLIP_MASK;
+ flip = readl(dev->regs + FIMC_REG_MSCTRL);
+ flip &= ~FIMC_REG_MSCTRL_FLIP_MASK;
flip |= fimc_hw_get_in_flip(ctx);
- writel(flip, dev->regs + S5P_MSCTRL);
+ writel(flip, dev->regs + FIMC_REG_MSCTRL);
}
}
@@ -110,43 +109,40 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx)
struct fimc_frame *frame = &ctx->d_frame;
dbg("w= %d, h= %d color: %d", frame->width,
- frame->height, frame->fmt->color);
+ frame->height, frame->fmt->color);
- cfg = readl(dev->regs + S5P_CITRGFMT);
- cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK |
- S5P_CITRGFMT_VSIZE_MASK);
+ cfg = readl(dev->regs + FIMC_REG_CITRGFMT);
+ cfg &= ~(FIMC_REG_CITRGFMT_FMT_MASK | FIMC_REG_CITRGFMT_HSIZE_MASK |
+ FIMC_REG_CITRGFMT_VSIZE_MASK);
switch (frame->fmt->color) {
- case S5P_FIMC_RGB444...S5P_FIMC_RGB888:
- cfg |= S5P_CITRGFMT_RGB;
+ case FIMC_FMT_RGB444...FIMC_FMT_RGB888:
+ cfg |= FIMC_REG_CITRGFMT_RGB;
break;
- case S5P_FIMC_YCBCR420:
- cfg |= S5P_CITRGFMT_YCBCR420;
+ case FIMC_FMT_YCBCR420:
+ cfg |= FIMC_REG_CITRGFMT_YCBCR420;
break;
- case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422:
+ case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422:
if (frame->fmt->colplanes == 1)
- cfg |= S5P_CITRGFMT_YCBCR422_1P;
+ cfg |= FIMC_REG_CITRGFMT_YCBCR422_1P;
else
- cfg |= S5P_CITRGFMT_YCBCR422;
+ cfg |= FIMC_REG_CITRGFMT_YCBCR422;
break;
default:
break;
}
- if (ctx->rotation == 90 || ctx->rotation == 270) {
- cfg |= S5P_CITRGFMT_HSIZE(frame->height);
- cfg |= S5P_CITRGFMT_VSIZE(frame->width);
- } else {
-
- cfg |= S5P_CITRGFMT_HSIZE(frame->width);
- cfg |= S5P_CITRGFMT_VSIZE(frame->height);
- }
+ if (ctx->rotation == 90 || ctx->rotation == 270)
+ cfg |= (frame->height << 16) | frame->width;
+ else
+ cfg |= (frame->width << 16) | frame->height;
- writel(cfg, dev->regs + S5P_CITRGFMT);
+ writel(cfg, dev->regs + FIMC_REG_CITRGFMT);
- cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK;
+ cfg = readl(dev->regs + FIMC_REG_CITAREA);
+ cfg &= ~FIMC_REG_CITAREA_MASK;
cfg |= (frame->width * frame->height);
- writel(cfg, dev->regs + S5P_CITAREA);
+ writel(cfg, dev->regs + FIMC_REG_CITAREA);
}
static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
@@ -155,87 +151,82 @@ static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx)
struct fimc_frame *frame = &ctx->d_frame;
u32 cfg;
- cfg = S5P_ORIG_SIZE_HOR(frame->f_width);
- cfg |= S5P_ORIG_SIZE_VER(frame->f_height);
- writel(cfg, dev->regs + S5P_ORGOSIZE);
+ cfg = (frame->f_height << 16) | frame->f_width;
+ writel(cfg, dev->regs + FIMC_REG_ORGOSIZE);
/* Select color space conversion equation (HD/SD size).*/
- cfg = readl(dev->regs + S5P_CIGCTRL);
+ cfg = readl(dev->regs + FIMC_REG_CIGCTRL);
if (frame->f_width >= 1280) /* HD */
- cfg |= S5P_CIGCTRL_CSC_ITU601_709;
+ cfg |= FIMC_REG_CIGCTRL_CSC_ITU601_709;
else /* SD */
- cfg &= ~S5P_CIGCTRL_CSC_ITU601_709;
- writel(cfg, dev->regs + S5P_CIGCTRL);
+ cfg &= ~FIMC_REG_CIGCTRL_CSC_ITU601_709;
+ writel(cfg, dev->regs + FIMC_REG_CIGCTRL);
}
void fimc_hw_set_out_dma(struct fimc_ctx *ctx)
{
- u32 cfg;
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_frame *frame = &ctx->d_frame;
struct fimc_dma_offset *offset = &frame->dma_offset;
struct fimc_fmt *fmt = frame->fmt;
+ u32 cfg;
/* Set the input dma offsets. */
- cfg = 0;
- cfg |= S5P_CIO_OFFS_HOR(offset->y_h);
- cfg |= S5P_CIO_OFFS_VER(offset->y_v);
- writel(cfg, dev->regs + S5P_CIOYOFF);
+ cfg = (offset->y_v << 16) | offset->y_h;
+ writel(cfg, dev->regs + FIMC_REG_CIOYOFF);
- cfg = 0;
- cfg |= S5P_CIO_OFFS_HOR(offset->cb_h);
- cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
- writel(cfg, dev->regs + S5P_CIOCBOFF);
+ cfg = (offset->cb_v << 16) | offset->cb_h;
+ writel(cfg, dev->regs + FIMC_REG_CIOCBOFF);
- cfg = 0;
- cfg |= S5P_CIO_OFFS_HOR(offset->cr_h);
- cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
- writel(cfg, dev->regs + S5P_CIOCROFF);
+ cfg = (offset->cr_v << 16) | offset->cr_h;
+ writel(cfg, dev->regs + FIMC_REG_CIOCROFF);
fimc_hw_set_out_dma_size(ctx);
/* Configure chroma components order. */
- cfg = readl(dev->regs + S5P_CIOCTRL);
+ cfg = readl(dev->regs + FIMC_REG_CIOCTRL);
- cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK |
- S5P_CIOCTRL_YCBCR_PLANE_MASK | S5P_CIOCTRL_RGB16FMT_MASK);
+ cfg &= ~(FIMC_REG_CIOCTRL_ORDER2P_MASK |
+ FIMC_REG_CIOCTRL_ORDER422_MASK |
+ FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK |
+ FIMC_REG_CIOCTRL_RGB16FMT_MASK);
if (fmt->colplanes == 1)
cfg |= ctx->out_order_1p;
else if (fmt->colplanes == 2)
- cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE;
+ cfg |= ctx->out_order_2p | FIMC_REG_CIOCTRL_YCBCR_2PLANE;
else if (fmt->colplanes == 3)
- cfg |= S5P_CIOCTRL_YCBCR_3PLANE;
+ cfg |= FIMC_REG_CIOCTRL_YCBCR_3PLANE;
- if (fmt->color == S5P_FIMC_RGB565)
- cfg |= S5P_CIOCTRL_RGB565;
- else if (fmt->color == S5P_FIMC_RGB555)
- cfg |= S5P_CIOCTRL_ARGB1555;
- else if (fmt->color == S5P_FIMC_RGB444)
- cfg |= S5P_CIOCTRL_ARGB4444;
+ if (fmt->color == FIMC_FMT_RGB565)
+ cfg |= FIMC_REG_CIOCTRL_RGB565;
+ else if (fmt->color == FIMC_FMT_RGB555)
+ cfg |= FIMC_REG_CIOCTRL_ARGB1555;
+ else if (fmt->color == FIMC_FMT_RGB444)
+ cfg |= FIMC_REG_CIOCTRL_ARGB4444;
- writel(cfg, dev->regs + S5P_CIOCTRL);
+ writel(cfg, dev->regs + FIMC_REG_CIOCTRL);
}
static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable)
{
- u32 cfg = readl(dev->regs + S5P_ORGISIZE);
+ u32 cfg = readl(dev->regs + FIMC_REG_ORGISIZE);
if (enable)
- cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ cfg |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN;
else
- cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN;
- writel(cfg, dev->regs + S5P_ORGISIZE);
+ cfg &= ~FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN;
+ writel(cfg, dev->regs + FIMC_REG_ORGISIZE);
}
void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable)
{
- u32 cfg = readl(dev->regs + S5P_CIOCTRL);
+ u32 cfg = readl(dev->regs + FIMC_REG_CIOCTRL);
if (enable)
- cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE;
+ cfg |= FIMC_REG_CIOCTRL_LASTIRQ_ENABLE;
else
- cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE;
- writel(cfg, dev->regs + S5P_CIOCTRL);
+ cfg &= ~FIMC_REG_CIOCTRL_LASTIRQ_ENABLE;
+ writel(cfg, dev->regs + FIMC_REG_CIOCTRL);
}
void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
@@ -245,15 +236,13 @@ void fimc_hw_set_prescaler(struct fimc_ctx *ctx)
u32 cfg, shfactor;
shfactor = 10 - (sc->hfactor + sc->vfactor);
+ cfg = shfactor << 28;
- cfg = S5P_CISCPRERATIO_SHFACTOR(shfactor);
- cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio);
- cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio);
- writel(cfg, dev->regs + S5P_CISCPRERATIO);
+ cfg |= (sc->pre_hratio << 16) | sc->pre_vratio;
+ writel(cfg, dev->regs + FIMC_REG_CISCPRERATIO);
- cfg = S5P_CISCPREDST_WIDTH(sc->pre_dst_width);
- cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height);
- writel(cfg, dev->regs + S5P_CISCPREDST);
+ cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height;
+ writel(cfg, dev->regs + FIMC_REG_CISCPREDST);
}
static void fimc_hw_set_scaler(struct fimc_ctx *ctx)
@@ -263,93 +252,95 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx)
struct fimc_frame *src_frame = &ctx->s_frame;
struct fimc_frame *dst_frame = &ctx->d_frame;
- u32 cfg = readl(dev->regs + S5P_CISCCTRL);
+ u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL);
- cfg &= ~(S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE |
- S5P_CISCCTRL_SCALEUP_H | S5P_CISCCTRL_SCALEUP_V |
- S5P_CISCCTRL_SCALERBYPASS | S5P_CISCCTRL_ONE2ONE |
- S5P_CISCCTRL_INRGB_FMT_MASK | S5P_CISCCTRL_OUTRGB_FMT_MASK |
- S5P_CISCCTRL_INTERLACE | S5P_CISCCTRL_RGB_EXT);
+ cfg &= ~(FIMC_REG_CISCCTRL_CSCR2Y_WIDE | FIMC_REG_CISCCTRL_CSCY2R_WIDE |
+ FIMC_REG_CISCCTRL_SCALEUP_H | FIMC_REG_CISCCTRL_SCALEUP_V |
+ FIMC_REG_CISCCTRL_SCALERBYPASS | FIMC_REG_CISCCTRL_ONE2ONE |
+ FIMC_REG_CISCCTRL_INRGB_FMT_MASK | FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK |
+ FIMC_REG_CISCCTRL_INTERLACE | FIMC_REG_CISCCTRL_RGB_EXT);
if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW))
- cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE);
+ cfg |= (FIMC_REG_CISCCTRL_CSCR2Y_WIDE |
+ FIMC_REG_CISCCTRL_CSCY2R_WIDE);
if (!sc->enabled)
- cfg |= S5P_CISCCTRL_SCALERBYPASS;
+ cfg |= FIMC_REG_CISCCTRL_SCALERBYPASS;
if (sc->scaleup_h)
- cfg |= S5P_CISCCTRL_SCALEUP_H;
+ cfg |= FIMC_REG_CISCCTRL_SCALEUP_H;
if (sc->scaleup_v)
- cfg |= S5P_CISCCTRL_SCALEUP_V;
+ cfg |= FIMC_REG_CISCCTRL_SCALEUP_V;
if (sc->copy_mode)
- cfg |= S5P_CISCCTRL_ONE2ONE;
+ cfg |= FIMC_REG_CISCCTRL_ONE2ONE;
- if (ctx->in_path == FIMC_DMA) {
+ if (ctx->in_path == FIMC_IO_DMA) {
switch (src_frame->fmt->color) {
- case S5P_FIMC_RGB565:
- cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565;
+ case FIMC_FMT_RGB565:
+ cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB565;
break;
- case S5P_FIMC_RGB666:
- cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666;
+ case FIMC_FMT_RGB666:
+ cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB666;
break;
- case S5P_FIMC_RGB888:
- cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888;
+ case FIMC_FMT_RGB888:
+ cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB888;
break;
}
}
- if (ctx->out_path == FIMC_DMA) {
+ if (ctx->out_path == FIMC_IO_DMA) {
u32 color = dst_frame->fmt->color;
- if (color >= S5P_FIMC_RGB444 && color <= S5P_FIMC_RGB565)
- cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565;
- else if (color == S5P_FIMC_RGB666)
- cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666;
- else if (color == S5P_FIMC_RGB888)
- cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+ if (color >= FIMC_FMT_RGB444 && color <= FIMC_FMT_RGB565)
+ cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565;
+ else if (color == FIMC_FMT_RGB666)
+ cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666;
+ else if (color == FIMC_FMT_RGB888)
+ cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888;
} else {
- cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888;
+ cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888;
if (ctx->flags & FIMC_SCAN_MODE_INTERLACED)
- cfg |= S5P_CISCCTRL_INTERLACE;
+ cfg |= FIMC_REG_CISCCTRL_INTERLACE;
}
- writel(cfg, dev->regs + S5P_CISCCTRL);
+ writel(cfg, dev->regs + FIMC_REG_CISCCTRL);
}
void fimc_hw_set_mainscaler(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
- struct samsung_fimc_variant *variant = dev->variant;
+ struct fimc_variant *variant = dev->variant;
struct fimc_scaler *sc = &ctx->scaler;
u32 cfg;
dbg("main_hratio= 0x%X main_vratio= 0x%X",
- sc->main_hratio, sc->main_vratio);
+ sc->main_hratio, sc->main_vratio);
fimc_hw_set_scaler(ctx);
- cfg = readl(dev->regs + S5P_CISCCTRL);
- cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK);
+ cfg = readl(dev->regs + FIMC_REG_CISCCTRL);
+ cfg &= ~(FIMC_REG_CISCCTRL_MHRATIO_MASK |
+ FIMC_REG_CISCCTRL_MVRATIO_MASK);
if (variant->has_mainscaler_ext) {
- cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio);
- cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio);
- writel(cfg, dev->regs + S5P_CISCCTRL);
+ cfg |= FIMC_REG_CISCCTRL_MHRATIO_EXT(sc->main_hratio);
+ cfg |= FIMC_REG_CISCCTRL_MVRATIO_EXT(sc->main_vratio);
+ writel(cfg, dev->regs + FIMC_REG_CISCCTRL);
- cfg = readl(dev->regs + S5P_CIEXTEN);
+ cfg = readl(dev->regs + FIMC_REG_CIEXTEN);
- cfg &= ~(S5P_CIEXTEN_MVRATIO_EXT_MASK |
- S5P_CIEXTEN_MHRATIO_EXT_MASK);
- cfg |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio);
- cfg |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio);
- writel(cfg, dev->regs + S5P_CIEXTEN);
+ cfg &= ~(FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK |
+ FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK);
+ cfg |= FIMC_REG_CIEXTEN_MHRATIO_EXT(sc->main_hratio);
+ cfg |= FIMC_REG_CIEXTEN_MVRATIO_EXT(sc->main_vratio);
+ writel(cfg, dev->regs + FIMC_REG_CIEXTEN);
} else {
- cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio);
- cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio);
- writel(cfg, dev->regs + S5P_CISCCTRL);
+ cfg |= FIMC_REG_CISCCTRL_MHRATIO(sc->main_hratio);
+ cfg |= FIMC_REG_CISCCTRL_MVRATIO(sc->main_vratio);
+ writel(cfg, dev->regs + FIMC_REG_CISCCTRL);
}
}
@@ -357,40 +348,41 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
- u32 cfg = readl(dev->regs + S5P_CIIMGCPT);
+ u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT);
- if (ctx->out_path == FIMC_DMA) {
+ if (ctx->out_path == FIMC_IO_DMA) {
/* one shot mode */
- cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN;
+ cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE |
+ FIMC_REG_CIIMGCPT_IMGCPTEN;
} else {
/* Continuous frame capture mode (freerun). */
- cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE |
- S5P_CIIMGCPT_CPT_FRMOD_CNT);
- cfg |= S5P_CIIMGCPT_IMGCPTEN;
+ cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE |
+ FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT);
+ cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN;
}
if (ctx->scaler.enabled)
- cfg |= S5P_CIIMGCPT_IMGCPTEN_SC;
+ cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC;
- writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT);
+ cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN;
+ writel(cfg, dev->regs + FIMC_REG_CIIMGCPT);
}
-void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active)
+void fimc_hw_set_effect(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
struct fimc_effect *effect = &ctx->effect;
u32 cfg = 0;
- if (active) {
- cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE;
+ if (effect->type != FIMC_REG_CIIMGEFF_FIN_BYPASS) {
+ cfg |= FIMC_REG_CIIMGEFF_IE_SC_AFTER |
+ FIMC_REG_CIIMGEFF_IE_ENABLE;
cfg |= effect->type;
- if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) {
- cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb);
- cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr);
- }
+ if (effect->type == FIMC_REG_CIIMGEFF_FIN_ARBITRARY)
+ cfg |= (effect->pat_cb << 13) | effect->pat_cr;
}
- writel(cfg, dev->regs + S5P_CIIMGEFF);
+ writel(cfg, dev->regs + FIMC_REG_CIIMGEFF);
}
void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx)
@@ -402,10 +394,10 @@ void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx)
if (!(frame->fmt->flags & FMT_HAS_ALPHA))
return;
- cfg = readl(dev->regs + S5P_CIOCTRL);
- cfg &= ~S5P_CIOCTRL_ALPHA_OUT_MASK;
+ cfg = readl(dev->regs + FIMC_REG_CIOCTRL);
+ cfg &= ~FIMC_REG_CIOCTRL_ALPHA_OUT_MASK;
cfg |= (frame->alpha << 4);
- writel(cfg, dev->regs + S5P_CIOCTRL);
+ writel(cfg, dev->regs + FIMC_REG_CIOCTRL);
}
static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
@@ -415,16 +407,14 @@ static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx)
u32 cfg_o = 0;
u32 cfg_r = 0;
- if (FIMC_LCDFIFO == ctx->out_path)
- cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN;
+ if (FIMC_IO_LCDFIFO == ctx->out_path)
+ cfg_r |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN;
- cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width);
- cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height);
- cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width);
- cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height);
+ cfg_o |= (frame->f_height << 16) | frame->f_width;
+ cfg_r |= (frame->height << 16) | frame->width;
- writel(cfg_o, dev->regs + S5P_ORGISIZE);
- writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE);
+ writel(cfg_o, dev->regs + FIMC_REG_ORGISIZE);
+ writel(cfg_r, dev->regs + FIMC_REG_CIREAL_ISIZE);
}
void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
@@ -435,80 +425,77 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx)
u32 cfg;
/* Set the pixel offsets. */
- cfg = S5P_CIO_OFFS_HOR(offset->y_h);
- cfg |= S5P_CIO_OFFS_VER(offset->y_v);
- writel(cfg, dev->regs + S5P_CIIYOFF);
+ cfg = (offset->y_v << 16) | offset->y_h;
+ writel(cfg, dev->regs + FIMC_REG_CIIYOFF);
- cfg = S5P_CIO_OFFS_HOR(offset->cb_h);
- cfg |= S5P_CIO_OFFS_VER(offset->cb_v);
- writel(cfg, dev->regs + S5P_CIICBOFF);
+ cfg = (offset->cb_v << 16) | offset->cb_h;
+ writel(cfg, dev->regs + FIMC_REG_CIICBOFF);
- cfg = S5P_CIO_OFFS_HOR(offset->cr_h);
- cfg |= S5P_CIO_OFFS_VER(offset->cr_v);
- writel(cfg, dev->regs + S5P_CIICROFF);
+ cfg = (offset->cr_v << 16) | offset->cr_h;
+ writel(cfg, dev->regs + FIMC_REG_CIICROFF);
/* Input original and real size. */
fimc_hw_set_in_dma_size(ctx);
/* Use DMA autoload only in FIFO mode. */
- fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO);
+ fimc_hw_en_autoload(dev, ctx->out_path == FIMC_IO_LCDFIFO);
/* Set the input DMA to process single frame only. */
- cfg = readl(dev->regs + S5P_MSCTRL);
- cfg &= ~(S5P_MSCTRL_INFORMAT_MASK
- | S5P_MSCTRL_IN_BURST_COUNT_MASK
- | S5P_MSCTRL_INPUT_MASK
- | S5P_MSCTRL_C_INT_IN_MASK
- | S5P_MSCTRL_2P_IN_ORDER_MASK);
+ cfg = readl(dev->regs + FIMC_REG_MSCTRL);
+ cfg &= ~(FIMC_REG_MSCTRL_INFORMAT_MASK
+ | FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK
+ | FIMC_REG_MSCTRL_INPUT_MASK
+ | FIMC_REG_MSCTRL_C_INT_IN_MASK
+ | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK);
- cfg |= (S5P_MSCTRL_IN_BURST_COUNT(4)
- | S5P_MSCTRL_INPUT_MEMORY
- | S5P_MSCTRL_FIFO_CTRL_FULL);
+ cfg |= (FIMC_REG_MSCTRL_IN_BURST_COUNT(4)
+ | FIMC_REG_MSCTRL_INPUT_MEMORY
+ | FIMC_REG_MSCTRL_FIFO_CTRL_FULL);
switch (frame->fmt->color) {
- case S5P_FIMC_RGB565...S5P_FIMC_RGB888:
- cfg |= S5P_MSCTRL_INFORMAT_RGB;
+ case FIMC_FMT_RGB565...FIMC_FMT_RGB888:
+ cfg |= FIMC_REG_MSCTRL_INFORMAT_RGB;
break;
- case S5P_FIMC_YCBCR420:
- cfg |= S5P_MSCTRL_INFORMAT_YCBCR420;
+ case FIMC_FMT_YCBCR420:
+ cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR420;
if (frame->fmt->colplanes == 2)
- cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE;
+ cfg |= ctx->in_order_2p | FIMC_REG_MSCTRL_C_INT_IN_2PLANE;
else
- cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+ cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE;
break;
- case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422:
+ case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422:
if (frame->fmt->colplanes == 1) {
cfg |= ctx->in_order_1p
- | S5P_MSCTRL_INFORMAT_YCBCR422_1P;
+ | FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P;
} else {
- cfg |= S5P_MSCTRL_INFORMAT_YCBCR422;
+ cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR422;
if (frame->fmt->colplanes == 2)
cfg |= ctx->in_order_2p
- | S5P_MSCTRL_C_INT_IN_2PLANE;
+ | FIMC_REG_MSCTRL_C_INT_IN_2PLANE;
else
- cfg |= S5P_MSCTRL_C_INT_IN_3PLANE;
+ cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE;
}
break;
default:
break;
}
- writel(cfg, dev->regs + S5P_MSCTRL);
+ writel(cfg, dev->regs + FIMC_REG_MSCTRL);
/* Input/output DMA linear/tiled mode. */
- cfg = readl(dev->regs + S5P_CIDMAPARAM);
- cfg &= ~S5P_CIDMAPARAM_TILE_MASK;
+ cfg = readl(dev->regs + FIMC_REG_CIDMAPARAM);
+ cfg &= ~FIMC_REG_CIDMAPARAM_TILE_MASK;
if (tiled_fmt(ctx->s_frame.fmt))
- cfg |= S5P_CIDMAPARAM_R_64X32;
+ cfg |= FIMC_REG_CIDMAPARAM_R_64X32;
if (tiled_fmt(ctx->d_frame.fmt))
- cfg |= S5P_CIDMAPARAM_W_64X32;
+ cfg |= FIMC_REG_CIDMAPARAM_W_64X32;
- writel(cfg, dev->regs + S5P_CIDMAPARAM);
+ writel(cfg, dev->regs + FIMC_REG_CIDMAPARAM);
}
@@ -516,40 +503,40 @@ void fimc_hw_set_input_path(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
- u32 cfg = readl(dev->regs + S5P_MSCTRL);
- cfg &= ~S5P_MSCTRL_INPUT_MASK;
+ u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL);
+ cfg &= ~FIMC_REG_MSCTRL_INPUT_MASK;
- if (ctx->in_path == FIMC_DMA)
- cfg |= S5P_MSCTRL_INPUT_MEMORY;
+ if (ctx->in_path == FIMC_IO_DMA)
+ cfg |= FIMC_REG_MSCTRL_INPUT_MEMORY;
else
- cfg |= S5P_MSCTRL_INPUT_EXTCAM;
+ cfg |= FIMC_REG_MSCTRL_INPUT_EXTCAM;
- writel(cfg, dev->regs + S5P_MSCTRL);
+ writel(cfg, dev->regs + FIMC_REG_MSCTRL);
}
void fimc_hw_set_output_path(struct fimc_ctx *ctx)
{
struct fimc_dev *dev = ctx->fimc_dev;
- u32 cfg = readl(dev->regs + S5P_CISCCTRL);
- cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO;
- if (ctx->out_path == FIMC_LCDFIFO)
- cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO;
- writel(cfg, dev->regs + S5P_CISCCTRL);
+ u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL);
+ cfg &= ~FIMC_REG_CISCCTRL_LCDPATHEN_FIFO;
+ if (ctx->out_path == FIMC_IO_LCDFIFO)
+ cfg |= FIMC_REG_CISCCTRL_LCDPATHEN_FIFO;
+ writel(cfg, dev->regs + FIMC_REG_CISCCTRL);
}
void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr)
{
- u32 cfg = readl(dev->regs + S5P_CIREAL_ISIZE);
- cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS;
- writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+ u32 cfg = readl(dev->regs + FIMC_REG_CIREAL_ISIZE);
+ cfg |= FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE);
- writel(paddr->y, dev->regs + S5P_CIIYSA(0));
- writel(paddr->cb, dev->regs + S5P_CIICBSA(0));
- writel(paddr->cr, dev->regs + S5P_CIICRSA(0));
+ writel(paddr->y, dev->regs + FIMC_REG_CIIYSA(0));
+ writel(paddr->cb, dev->regs + FIMC_REG_CIICBSA(0));
+ writel(paddr->cr, dev->regs + FIMC_REG_CIICRSA(0));
- cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS;
- writel(cfg, dev->regs + S5P_CIREAL_ISIZE);
+ cfg &= ~FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS;
+ writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE);
}
void fimc_hw_set_output_addr(struct fimc_dev *dev,
@@ -557,9 +544,9 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev,
{
int i = (index == -1) ? 0 : index;
do {
- writel(paddr->y, dev->regs + S5P_CIOYSA(i));
- writel(paddr->cb, dev->regs + S5P_CIOCBSA(i));
- writel(paddr->cr, dev->regs + S5P_CIOCRSA(i));
+ writel(paddr->y, dev->regs + FIMC_REG_CIOYSA(i));
+ writel(paddr->cb, dev->regs + FIMC_REG_CIOCBSA(i));
+ writel(paddr->cr, dev->regs + FIMC_REG_CIOCRSA(i));
dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X",
i, paddr->y, paddr->cb, paddr->cr);
} while (index == -1 && ++i < FIMC_MAX_OUT_BUFS);
@@ -568,32 +555,45 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev,
int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
struct s5p_fimc_isp_info *cam)
{
- u32 cfg = readl(fimc->regs + S5P_CIGCTRL);
+ u32 cfg = readl(fimc->regs + FIMC_REG_CIGCTRL);
- cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC |
- S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC |
- S5P_CIGCTRL_INVPOLFIELD);
+ cfg &= ~(FIMC_REG_CIGCTRL_INVPOLPCLK | FIMC_REG_CIGCTRL_INVPOLVSYNC |
+ FIMC_REG_CIGCTRL_INVPOLHREF | FIMC_REG_CIGCTRL_INVPOLHSYNC |
+ FIMC_REG_CIGCTRL_INVPOLFIELD);
if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- cfg |= S5P_CIGCTRL_INVPOLPCLK;
+ cfg |= FIMC_REG_CIGCTRL_INVPOLPCLK;
if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- cfg |= S5P_CIGCTRL_INVPOLVSYNC;
+ cfg |= FIMC_REG_CIGCTRL_INVPOLVSYNC;
if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- cfg |= S5P_CIGCTRL_INVPOLHREF;
+ cfg |= FIMC_REG_CIGCTRL_INVPOLHREF;
if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- cfg |= S5P_CIGCTRL_INVPOLHSYNC;
+ cfg |= FIMC_REG_CIGCTRL_INVPOLHSYNC;
if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW)
- cfg |= S5P_CIGCTRL_INVPOLFIELD;
+ cfg |= FIMC_REG_CIGCTRL_INVPOLFIELD;
- writel(cfg, fimc->regs + S5P_CIGCTRL);
+ writel(cfg, fimc->regs + FIMC_REG_CIGCTRL);
return 0;
}
+struct mbus_pixfmt_desc {
+ u32 pixelcode;
+ u32 cisrcfmt;
+ u16 bus_width;
+};
+
+static const struct mbus_pixfmt_desc pix_desc[] = {
+ { V4L2_MBUS_FMT_YUYV8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCBYCR, 8 },
+ { V4L2_MBUS_FMT_YVYU8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCRYCB, 8 },
+ { V4L2_MBUS_FMT_VYUY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CRYCBY, 8 },
+ { V4L2_MBUS_FMT_UYVY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CBYCRY, 8 },
+};
+
int fimc_hw_set_camera_source(struct fimc_dev *fimc,
struct s5p_fimc_isp_info *cam)
{
@@ -602,18 +602,6 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
u32 bus_width;
int i;
- static const struct {
- u32 pixelcode;
- u32 cisrcfmt;
- u16 bus_width;
- } pix_desc[] = {
- { V4L2_MBUS_FMT_YUYV8_2X8, S5P_CISRCFMT_ORDER422_YCBYCR, 8 },
- { V4L2_MBUS_FMT_YVYU8_2X8, S5P_CISRCFMT_ORDER422_YCRYCB, 8 },
- { V4L2_MBUS_FMT_VYUY8_2X8, S5P_CISRCFMT_ORDER422_CRYCBY, 8 },
- { V4L2_MBUS_FMT_UYVY8_2X8, S5P_CISRCFMT_ORDER422_CBYCRY, 8 },
- /* TODO: Add pixel codes for 16-bit bus width */
- };
-
if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) {
for (i = 0; i < ARRAY_SIZE(pix_desc); i++) {
if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) {
@@ -632,41 +620,37 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc,
if (cam->bus_type == FIMC_ITU_601) {
if (bus_width == 8)
- cfg |= S5P_CISRCFMT_ITU601_8BIT;
+ cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT;
else if (bus_width == 16)
- cfg |= S5P_CISRCFMT_ITU601_16BIT;
+ cfg |= FIMC_REG_CISRCFMT_ITU601_16BIT;
} /* else defaults to ITU-R BT.656 8-bit */
} else if (cam->bus_type == FIMC_MIPI_CSI2) {
if (fimc_fmt_is_jpeg(f->fmt->color))
- cfg |= S5P_CISRCFMT_ITU601_8BIT;
+ cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT;
}
- cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height);
- writel(cfg, fimc->regs + S5P_CISRCFMT);
+ cfg |= (f->o_width << 16) | f->o_height;
+ writel(cfg, fimc->regs + FIMC_REG_CISRCFMT);
return 0;
}
-
-int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f)
+void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f)
{
u32 hoff2, voff2;
- u32 cfg = readl(fimc->regs + S5P_CIWDOFST);
+ u32 cfg = readl(fimc->regs + FIMC_REG_CIWDOFST);
- cfg &= ~(S5P_CIWDOFST_HOROFF_MASK | S5P_CIWDOFST_VEROFF_MASK);
- cfg |= S5P_CIWDOFST_OFF_EN |
- S5P_CIWDOFST_HOROFF(f->offs_h) |
- S5P_CIWDOFST_VEROFF(f->offs_v);
+ cfg &= ~(FIMC_REG_CIWDOFST_HOROFF_MASK | FIMC_REG_CIWDOFST_VEROFF_MASK);
+ cfg |= FIMC_REG_CIWDOFST_OFF_EN |
+ (f->offs_h << 16) | f->offs_v;
- writel(cfg, fimc->regs + S5P_CIWDOFST);
+ writel(cfg, fimc->regs + FIMC_REG_CIWDOFST);
/* See CIWDOFSTn register description in the datasheet for details. */
hoff2 = f->o_width - f->width - f->offs_h;
voff2 = f->o_height - f->height - f->offs_v;
- cfg = S5P_CIWDOFST2_HOROFF(hoff2) | S5P_CIWDOFST2_VEROFF(voff2);
-
- writel(cfg, fimc->regs + S5P_CIWDOFST2);
- return 0;
+ cfg = (hoff2 << 16) | voff2;
+ writel(cfg, fimc->regs + FIMC_REG_CIWDOFST2);
}
int fimc_hw_set_camera_type(struct fimc_dev *fimc,
@@ -674,28 +658,29 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
{
u32 cfg, tmp;
struct fimc_vid_cap *vid_cap = &fimc->vid_cap;
+ u32 csis_data_alignment = 32;
- cfg = readl(fimc->regs + S5P_CIGCTRL);
+ cfg = readl(fimc->regs + FIMC_REG_CIGCTRL);
/* Select ITU B interface, disable Writeback path and test pattern. */
- cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A |
- S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB |
- S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG);
+ cfg &= ~(FIMC_REG_CIGCTRL_TESTPAT_MASK | FIMC_REG_CIGCTRL_SELCAM_ITU_A |
+ FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB |
+ FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG);
if (cam->bus_type == FIMC_MIPI_CSI2) {
- cfg |= S5P_CIGCTRL_SELCAM_MIPI;
+ cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI;
if (cam->mux_id == 0)
- cfg |= S5P_CIGCTRL_SELCAM_MIPI_A;
+ cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A;
/* TODO: add remaining supported formats. */
switch (vid_cap->mf.code) {
case V4L2_MBUS_FMT_VYUY8_2X8:
- tmp = S5P_CSIIMGFMT_YCBCR422_8BIT;
+ tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT;
break;
case V4L2_MBUS_FMT_JPEG_1X8:
- tmp = S5P_CSIIMGFMT_USER(1);
- cfg |= S5P_CIGCTRL_CAM_JPEG;
+ tmp = FIMC_REG_CSIIMGFMT_USER(1);
+ cfg |= FIMC_REG_CIGCTRL_CAM_JPEG;
break;
default:
v4l2_err(fimc->vid_cap.vfd,
@@ -703,21 +688,86 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc,
vid_cap->mf.code);
return -EINVAL;
}
- tmp |= (cam->csi_data_align == 32) << 8;
+ tmp |= (csis_data_alignment == 32) << 8;
- writel(tmp, fimc->regs + S5P_CSIIMGFMT);
+ writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT);
} else if (cam->bus_type == FIMC_ITU_601 ||
cam->bus_type == FIMC_ITU_656) {
if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */
- cfg |= S5P_CIGCTRL_SELCAM_ITU_A;
+ cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A;
} else if (cam->bus_type == FIMC_LCD_WB) {
- cfg |= S5P_CIGCTRL_CAMIF_SELWB;
+ cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB;
} else {
err("invalid camera bus type selected\n");
return -EINVAL;
}
- writel(cfg, fimc->regs + S5P_CIGCTRL);
+ writel(cfg, fimc->regs + FIMC_REG_CIGCTRL);
return 0;
}
+
+void fimc_hw_clear_irq(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + FIMC_REG_CIGCTRL);
+ cfg |= FIMC_REG_CIGCTRL_IRQ_CLR;
+ writel(cfg, dev->regs + FIMC_REG_CIGCTRL);
+}
+
+void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on)
+{
+ u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL);
+ if (on)
+ cfg |= FIMC_REG_CISCCTRL_SCALERSTART;
+ else
+ cfg &= ~FIMC_REG_CISCCTRL_SCALERSTART;
+ writel(cfg, dev->regs + FIMC_REG_CISCCTRL);
+}
+
+void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on)
+{
+ u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL);
+ if (on)
+ cfg |= FIMC_REG_MSCTRL_ENVID;
+ else
+ cfg &= ~FIMC_REG_MSCTRL_ENVID;
+ writel(cfg, dev->regs + FIMC_REG_MSCTRL);
+}
+
+void fimc_hw_dis_capture(struct fimc_dev *dev)
+{
+ u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT);
+ cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC);
+ writel(cfg, dev->regs + FIMC_REG_CIIMGCPT);
+}
+
+/* Return an index to the buffer actually being written. */
+u32 fimc_hw_get_frame_index(struct fimc_dev *dev)
+{
+ u32 reg;
+
+ if (dev->variant->has_cistatus2) {
+ reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3F;
+ return reg > 0 ? --reg : reg;
+ }
+
+ reg = readl(dev->regs + FIMC_REG_CISTATUS);
+
+ return (reg & FIMC_REG_CISTATUS_FRAMECNT_MASK) >>
+ FIMC_REG_CISTATUS_FRAMECNT_SHIFT;
+}
+
+/* Locking: the caller holds fimc->slock */
+void fimc_activate_capture(struct fimc_ctx *ctx)
+{
+ fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled);
+ fimc_hw_en_capture(ctx);
+}
+
+void fimc_deactivate_capture(struct fimc_dev *fimc)
+{
+ fimc_hw_en_lastirq(fimc, true);
+ fimc_hw_dis_capture(fimc);
+ fimc_hw_enable_scaler(fimc, false);
+ fimc_hw_en_lastirq(fimc, false);
+}
diff --git a/drivers/media/video/s5p-fimc/fimc-reg.h b/drivers/media/video/s5p-fimc/fimc-reg.h
new file mode 100644
index 00000000000..579ac8ac03d
--- /dev/null
+++ b/drivers/media/video/s5p-fimc/fimc-reg.h
@@ -0,0 +1,326 @@
+/*
+ * Samsung camera host interface (FIMC) registers definition
+ *
+ * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef FIMC_REG_H_
+#define FIMC_REG_H_
+
+#include "fimc-core.h"
+
+/* Input source format */
+#define FIMC_REG_CISRCFMT 0x00
+#define FIMC_REG_CISRCFMT_ITU601_8BIT (1 << 31)
+#define FIMC_REG_CISRCFMT_ITU601_16BIT (1 << 29)
+#define FIMC_REG_CISRCFMT_ORDER422_YCBYCR (0 << 14)
+#define FIMC_REG_CISRCFMT_ORDER422_YCRYCB (1 << 14)
+#define FIMC_REG_CISRCFMT_ORDER422_CBYCRY (2 << 14)
+#define FIMC_REG_CISRCFMT_ORDER422_CRYCBY (3 << 14)
+
+/* Window offset */
+#define FIMC_REG_CIWDOFST 0x04
+#define FIMC_REG_CIWDOFST_OFF_EN (1 << 31)
+#define FIMC_REG_CIWDOFST_CLROVFIY (1 << 30)
+#define FIMC_REG_CIWDOFST_CLROVRLB (1 << 29)
+#define FIMC_REG_CIWDOFST_HOROFF_MASK (0x7ff << 16)
+#define FIMC_REG_CIWDOFST_CLROVFICB (1 << 15)
+#define FIMC_REG_CIWDOFST_CLROVFICR (1 << 14)
+#define FIMC_REG_CIWDOFST_VEROFF_MASK (0xfff << 0)
+
+/* Global control */
+#define FIMC_REG_CIGCTRL 0x08
+#define FIMC_REG_CIGCTRL_SWRST (1 << 31)
+#define FIMC_REG_CIGCTRL_CAMRST_A (1 << 30)
+#define FIMC_REG_CIGCTRL_SELCAM_ITU_A (1 << 29)
+#define FIMC_REG_CIGCTRL_TESTPAT_NORMAL (0 << 27)
+#define FIMC_REG_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27)
+#define FIMC_REG_CIGCTRL_TESTPAT_HOR_INC (2 << 27)
+#define FIMC_REG_CIGCTRL_TESTPAT_VER_INC (3 << 27)
+#define FIMC_REG_CIGCTRL_TESTPAT_MASK (3 << 27)
+#define FIMC_REG_CIGCTRL_TESTPAT_SHIFT 27
+#define FIMC_REG_CIGCTRL_INVPOLPCLK (1 << 26)
+#define FIMC_REG_CIGCTRL_INVPOLVSYNC (1 << 25)
+#define FIMC_REG_CIGCTRL_INVPOLHREF (1 << 24)
+#define FIMC_REG_CIGCTRL_IRQ_OVFEN (1 << 22)
+#define FIMC_REG_CIGCTRL_HREF_MASK (1 << 21)
+#define FIMC_REG_CIGCTRL_IRQ_LEVEL (1 << 20)
+#define FIMC_REG_CIGCTRL_IRQ_CLR (1 << 19)
+#define FIMC_REG_CIGCTRL_IRQ_ENABLE (1 << 16)
+#define FIMC_REG_CIGCTRL_SHDW_DISABLE (1 << 12)
+#define FIMC_REG_CIGCTRL_CAM_JPEG (1 << 8)
+#define FIMC_REG_CIGCTRL_SELCAM_MIPI_A (1 << 7)
+#define FIMC_REG_CIGCTRL_CAMIF_SELWB (1 << 6)
+/* 0 - ITU601; 1 - ITU709 */
+#define FIMC_REG_CIGCTRL_CSC_ITU601_709 (1 << 5)
+#define FIMC_REG_CIGCTRL_INVPOLHSYNC (1 << 4)
+#define FIMC_REG_CIGCTRL_SELCAM_MIPI (1 << 3)
+#define FIMC_REG_CIGCTRL_INVPOLFIELD (1 << 1)
+#define FIMC_REG_CIGCTRL_INTERLACE (1 << 0)
+
+/* Window offset 2 */
+#define FIMC_REG_CIWDOFST2 0x14
+#define FIMC_REG_CIWDOFST2_HOROFF_MASK (0xfff << 16)
+#define FIMC_REG_CIWDOFST2_VEROFF_MASK (0xfff << 0)
+
+/* Output DMA Y/Cb/Cr plane start addresses */
+#define FIMC_REG_CIOYSA(n) (0x18 + (n) * 4)
+#define FIMC_REG_CIOCBSA(n) (0x28 + (n) * 4)
+#define FIMC_REG_CIOCRSA(n) (0x38 + (n) * 4)
+
+/* Target image format */
+#define FIMC_REG_CITRGFMT 0x48
+#define FIMC_REG_CITRGFMT_INROT90 (1 << 31)
+#define FIMC_REG_CITRGFMT_YCBCR420 (0 << 29)
+#define FIMC_REG_CITRGFMT_YCBCR422 (1 << 29)
+#define FIMC_REG_CITRGFMT_YCBCR422_1P (2 << 29)
+#define FIMC_REG_CITRGFMT_RGB (3 << 29)
+#define FIMC_REG_CITRGFMT_FMT_MASK (3 << 29)
+#define FIMC_REG_CITRGFMT_HSIZE_MASK (0xfff << 16)
+#define FIMC_REG_CITRGFMT_FLIP_SHIFT 14
+#define FIMC_REG_CITRGFMT_FLIP_NORMAL (0 << 14)
+#define FIMC_REG_CITRGFMT_FLIP_X_MIRROR (1 << 14)
+#define FIMC_REG_CITRGFMT_FLIP_Y_MIRROR (2 << 14)
+#define FIMC_REG_CITRGFMT_FLIP_180 (3 << 14)
+#define FIMC_REG_CITRGFMT_FLIP_MASK (3 << 14)
+#define FIMC_REG_CITRGFMT_OUTROT90 (1 << 13)
+#define FIMC_REG_CITRGFMT_VSIZE_MASK (0xfff << 0)
+
+/* Output DMA control */
+#define FIMC_REG_CIOCTRL 0x4c
+#define FIMC_REG_CIOCTRL_ORDER422_MASK (3 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (0 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (1 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (2 << 0)
+#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (3 << 0)
+#define FIMC_REG_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
+#define FIMC_REG_CIOCTRL_YCBCR_3PLANE (0 << 3)
+#define FIMC_REG_CIOCTRL_YCBCR_2PLANE (1 << 3)
+#define FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK (1 << 3)
+#define FIMC_REG_CIOCTRL_ALPHA_OUT_MASK (0xff << 4)
+#define FIMC_REG_CIOCTRL_RGB16FMT_MASK (3 << 16)
+#define FIMC_REG_CIOCTRL_RGB565 (0 << 16)
+#define FIMC_REG_CIOCTRL_ARGB1555 (1 << 16)
+#define FIMC_REG_CIOCTRL_ARGB4444 (2 << 16)
+#define FIMC_REG_CIOCTRL_ORDER2P_SHIFT 24
+#define FIMC_REG_CIOCTRL_ORDER2P_MASK (3 << 24)
+#define FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24)
+
+/* Pre-scaler control 1 */
+#define FIMC_REG_CISCPRERATIO 0x50
+
+#define FIMC_REG_CISCPREDST 0x54
+
+/* Main scaler control */
+#define FIMC_REG_CISCCTRL 0x58
+#define FIMC_REG_CISCCTRL_SCALERBYPASS (1 << 31)
+#define FIMC_REG_CISCCTRL_SCALEUP_H (1 << 30)
+#define FIMC_REG_CISCCTRL_SCALEUP_V (1 << 29)
+#define FIMC_REG_CISCCTRL_CSCR2Y_WIDE (1 << 28)
+#define FIMC_REG_CISCCTRL_CSCY2R_WIDE (1 << 27)
+#define FIMC_REG_CISCCTRL_LCDPATHEN_FIFO (1 << 26)
+#define FIMC_REG_CISCCTRL_INTERLACE (1 << 25)
+#define FIMC_REG_CISCCTRL_SCALERSTART (1 << 15)
+#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB565 (0 << 13)
+#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB666 (1 << 13)
+#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB888 (2 << 13)
+#define FIMC_REG_CISCCTRL_INRGB_FMT_MASK (3 << 13)
+#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
+#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
+#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
+#define FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
+#define FIMC_REG_CISCCTRL_RGB_EXT (1 << 10)
+#define FIMC_REG_CISCCTRL_ONE2ONE (1 << 9)
+#define FIMC_REG_CISCCTRL_MHRATIO(x) ((x) << 16)
+#define FIMC_REG_CISCCTRL_MVRATIO(x) ((x) << 0)
+#define FIMC_REG_CISCCTRL_MHRATIO_MASK (0x1ff << 16)
+#define FIMC_REG_CISCCTRL_MVRATIO_MASK (0x1ff << 0)
+#define FIMC_REG_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16)
+#define FIMC_REG_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0)
+
+/* Target area */
+#define FIMC_REG_CITAREA 0x5c
+#define FIMC_REG_CITAREA_MASK 0x0fffffff
+
+/* General status */
+#define FIMC_REG_CISTATUS 0x64
+#define FIMC_REG_CISTATUS_OVFIY (1 << 31)
+#define FIMC_REG_CISTATUS_OVFICB (1 << 30)
+#define FIMC_REG_CISTATUS_OVFICR (1 << 29)
+#define FIMC_REG_CISTATUS_VSYNC (1 << 28)
+#define FIMC_REG_CISTATUS_FRAMECNT_MASK (3 << 26)
+#define FIMC_REG_CISTATUS_FRAMECNT_SHIFT 26
+#define FIMC_REG_CISTATUS_WINOFF_EN (1 << 25)
+#define FIMC_REG_CISTATUS_IMGCPT_EN (1 << 22)
+#define FIMC_REG_CISTATUS_IMGCPT_SCEN (1 << 21)
+#define FIMC_REG_CISTATUS_VSYNC_A (1 << 20)
+#define FIMC_REG_CISTATUS_VSYNC_B (1 << 19)
+#define FIMC_REG_CISTATUS_OVRLB (1 << 18)
+#define FIMC_REG_CISTATUS_FRAME_END (1 << 17)
+#define FIMC_REG_CISTATUS_LASTCAPT_END (1 << 16)
+#define FIMC_REG_CISTATUS_VVALID_A (1 << 15)
+#define FIMC_REG_CISTATUS_VVALID_B (1 << 14)
+
+/* Indexes to the last and the currently processed buffer. */
+#define FIMC_REG_CISTATUS2 0x68
+
+/* Image capture control */
+#define FIMC_REG_CIIMGCPT 0xc0
+#define FIMC_REG_CIIMGCPT_IMGCPTEN (1 << 31)
+#define FIMC_REG_CIIMGCPT_IMGCPTEN_SC (1 << 30)
+#define FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE (1 << 25)
+#define FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT (1 << 18)
+
+/* Frame capture sequence */
+#define FIMC_REG_CICPTSEQ 0xc4
+
+/* Image effect */
+#define FIMC_REG_CIIMGEFF 0xd0
+#define FIMC_REG_CIIMGEFF_IE_ENABLE (1 << 30)
+#define FIMC_REG_CIIMGEFF_IE_SC_BEFORE (0 << 29)
+#define FIMC_REG_CIIMGEFF_IE_SC_AFTER (1 << 29)
+#define FIMC_REG_CIIMGEFF_FIN_BYPASS (0 << 26)
+#define FIMC_REG_CIIMGEFF_FIN_ARBITRARY (1 << 26)
+#define FIMC_REG_CIIMGEFF_FIN_NEGATIVE (2 << 26)
+#define FIMC_REG_CIIMGEFF_FIN_ARTFREEZE (3 << 26)
+#define FIMC_REG_CIIMGEFF_FIN_EMBOSSING (4 << 26)
+#define FIMC_REG_CIIMGEFF_FIN_SILHOUETTE (5 << 26)
+#define FIMC_REG_CIIMGEFF_FIN_MASK (7 << 26)
+#define FIMC_REG_CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | 0xff)
+
+/* Input DMA Y/Cb/Cr plane start address 0/1 */
+#define FIMC_REG_CIIYSA(n) (0xd4 + (n) * 0x70)
+#define FIMC_REG_CIICBSA(n) (0xd8 + (n) * 0x70)
+#define FIMC_REG_CIICRSA(n) (0xdc + (n) * 0x70)
+
+/* Real input DMA image size */
+#define FIMC_REG_CIREAL_ISIZE 0xf8
+#define FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31)
+#define FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30)
+
+/* Input DMA control */
+#define FIMC_REG_MSCTRL 0xfc
+#define FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK (0xf << 24)
+#define FIMC_REG_MSCTRL_2P_IN_ORDER_MASK (3 << 16)
+#define FIMC_REG_MSCTRL_2P_IN_ORDER_SHIFT 16
+#define FIMC_REG_MSCTRL_C_INT_IN_3PLANE (0 << 15)
+#define FIMC_REG_MSCTRL_C_INT_IN_2PLANE (1 << 15)
+#define FIMC_REG_MSCTRL_C_INT_IN_MASK (1 << 15)
+#define FIMC_REG_MSCTRL_FLIP_SHIFT 13
+#define FIMC_REG_MSCTRL_FLIP_MASK (3 << 13)
+#define FIMC_REG_MSCTRL_FLIP_NORMAL (0 << 13)
+#define FIMC_REG_MSCTRL_FLIP_X_MIRROR (1 << 13)
+#define FIMC_REG_MSCTRL_FLIP_Y_MIRROR (2 << 13)
+#define FIMC_REG_MSCTRL_FLIP_180 (3 << 13)
+#define FIMC_REG_MSCTRL_FIFO_CTRL_FULL (1 << 12)
+#define FIMC_REG_MSCTRL_ORDER422_SHIFT 4
+#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (0 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (1 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (2 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (3 << 4)
+#define FIMC_REG_MSCTRL_ORDER422_MASK (3 << 4)
+#define FIMC_REG_MSCTRL_INPUT_EXTCAM (0 << 3)
+#define FIMC_REG_MSCTRL_INPUT_MEMORY (1 << 3)
+#define FIMC_REG_MSCTRL_INPUT_MASK (1 << 3)
+#define FIMC_REG_MSCTRL_INFORMAT_YCBCR420 (0 << 1)
+#define FIMC_REG_MSCTRL_INFORMAT_YCBCR422 (1 << 1)
+#define FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1)
+#define FIMC_REG_MSCTRL_INFORMAT_RGB (3 << 1)
+#define FIMC_REG_MSCTRL_INFORMAT_MASK (3 << 1)
+#define FIMC_REG_MSCTRL_ENVID (1 << 0)
+#define FIMC_REG_MSCTRL_IN_BURST_COUNT(x) ((x) << 24)
+
+/* Output DMA Y/Cb/Cr offset */
+#define FIMC_REG_CIOYOFF 0x168
+#define FIMC_REG_CIOCBOFF 0x16c
+#define FIMC_REG_CIOCROFF 0x170
+
+/* Input DMA Y/Cb/Cr offset */
+#define FIMC_REG_CIIYOFF 0x174
+#define FIMC_REG_CIICBOFF 0x178
+#define FIMC_REG_CIICROFF 0x17c
+
+/* Input DMA original image size */
+#define FIMC_REG_ORGISIZE 0x180
+
+/* Output DMA original image size */
+#define FIMC_REG_ORGOSIZE 0x184
+
+/* Real output DMA image size (extension register) */
+#define FIMC_REG_CIEXTEN 0x188
+#define FIMC_REG_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10)
+#define FIMC_REG_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f)
+#define FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10)
+#define FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK 0x3f
+
+#define FIMC_REG_CIDMAPARAM 0x18c
+#define FIMC_REG_CIDMAPARAM_R_LINEAR (0 << 29)
+#define FIMC_REG_CIDMAPARAM_R_64X32 (3 << 29)
+#define FIMC_REG_CIDMAPARAM_W_LINEAR (0 << 13)
+#define FIMC_REG_CIDMAPARAM_W_64X32 (3 << 13)
+#define FIMC_REG_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13))
+
+/* MIPI CSI image format */
+#define FIMC_REG_CSIIMGFMT 0x194
+#define FIMC_REG_CSIIMGFMT_YCBCR422_8BIT 0x1e
+#define FIMC_REG_CSIIMGFMT_RAW8 0x2a
+#define FIMC_REG_CSIIMGFMT_RAW10 0x2b
+#define FIMC_REG_CSIIMGFMT_RAW12 0x2c
+/* User defined formats. x = 0...16. */
+#define FIMC_REG_CSIIMGFMT_USER(x) (0x30 + x - 1)
+
+/* Output frame buffer sequence mask */
+#define FIMC_REG_CIFCNTSEQ 0x1fc
+
+/*
+ * Function declarations
+ */
+void fimc_hw_reset(struct fimc_dev *fimc);
+void fimc_hw_set_rotation(struct fimc_ctx *ctx);
+void fimc_hw_set_target_format(struct fimc_ctx *ctx);
+void fimc_hw_set_out_dma(struct fimc_ctx *ctx);
+void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable);
+void fimc_hw_en_irq(struct fimc_dev *fimc, int enable);
+void fimc_hw_set_prescaler(struct fimc_ctx *ctx);
+void fimc_hw_set_mainscaler(struct fimc_ctx *ctx);
+void fimc_hw_en_capture(struct fimc_ctx *ctx);
+void fimc_hw_set_effect(struct fimc_ctx *ctx);
+void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx);
+void fimc_hw_set_in_dma(struct fimc_ctx *ctx);
+void fimc_hw_set_input_path(struct fimc_ctx *ctx);
+void fimc_hw_set_output_path(struct fimc_ctx *ctx);
+void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr);
+void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr,
+ int index);
+int fimc_hw_set_camera_source(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam);
+void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f);
+int fimc_hw_set_camera_polarity(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam);
+int fimc_hw_set_camera_type(struct fimc_dev *fimc,
+ struct s5p_fimc_isp_info *cam);
+void fimc_hw_clear_irq(struct fimc_dev *dev);
+void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on);
+void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on);
+void fimc_hw_dis_capture(struct fimc_dev *dev);
+u32 fimc_hw_get_frame_index(struct fimc_dev *dev);
+void fimc_activate_capture(struct fimc_ctx *ctx);
+void fimc_deactivate_capture(struct fimc_dev *fimc);
+
+/**
+ * fimc_hw_set_dma_seq - configure output DMA buffer sequence
+ * @mask: bitmask for the DMA output buffer registers, set to 0 to skip buffer
+ * This function masks output DMA ring buffers, it allows to select which of
+ * the 32 available output buffer address registers will be used by the DMA
+ * engine.
+ */
+static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask)
+{
+ writel(mask, dev->regs + FIMC_REG_CIFCNTSEQ);
+}
+
+#endif /* FIMC_REG_H_ */
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c
index f44f690397f..2f73d9e3d0b 100644
--- a/drivers/media/video/s5p-fimc/mipi-csis.c
+++ b/drivers/media/video/s5p-fimc/mipi-csis.c
@@ -127,20 +127,24 @@ struct csis_state {
* multiple of 2^pix_width_alignment
* @code: corresponding media bus code
* @fmt_reg: S5PCSIS_CONFIG register value
+ * @data_alignment: MIPI-CSI data alignment in bits
*/
struct csis_pix_format {
unsigned int pix_width_alignment;
enum v4l2_mbus_pixelcode code;
u32 fmt_reg;
+ u8 data_alignment;
};
static const struct csis_pix_format s5pcsis_formats[] = {
{
.code = V4L2_MBUS_FMT_VYUY8_2X8,
.fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 32,
}, {
.code = V4L2_MBUS_FMT_JPEG_1X8,
.fmt_reg = S5PCSIS_CFG_FMT_USER(1),
+ .data_alignment = 32,
},
};
@@ -239,7 +243,7 @@ static void s5pcsis_set_params(struct csis_state *state)
s5pcsis_set_hsync_settle(state, pdata->hs_settle);
val = s5pcsis_read(state, S5PCSIS_CTRL);
- if (pdata->alignment == 32)
+ if (state->csis_fmt->data_alignment == 32)
val |= S5PCSIS_CTRL_ALIGN_32BIT;
else /* 24-bits */
val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
@@ -711,19 +715,8 @@ static struct platform_driver s5pcsis_driver = {
},
};
-static int __init s5pcsis_init(void)
-{
- return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe);
-}
-
-static void __exit s5pcsis_exit(void)
-{
- platform_driver_unregister(&s5pcsis_driver);
-}
-
-module_init(s5pcsis_init);
-module_exit(s5pcsis_exit);
+module_platform_driver(s5pcsis_driver);
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
-MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver");
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h
deleted file mode 100644
index c7a5bc51d57..00000000000
--- a/drivers/media/video/s5p-fimc/regs-fimc.h
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Register definition file for Samsung Camera Interface (FIMC) driver
- *
- * Copyright (c) 2010 Samsung Electronics
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef REGS_FIMC_H_
-#define REGS_FIMC_H_
-
-/* Input source format */
-#define S5P_CISRCFMT 0x00
-#define S5P_CISRCFMT_ITU601_8BIT (1 << 31)
-#define S5P_CISRCFMT_ITU601_16BIT (1 << 29)
-#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14)
-#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14)
-#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14)
-#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14)
-#define S5P_CISRCFMT_HSIZE(x) ((x) << 16)
-#define S5P_CISRCFMT_VSIZE(x) ((x) << 0)
-
-/* Window offset */
-#define S5P_CIWDOFST 0x04
-#define S5P_CIWDOFST_OFF_EN (1 << 31)
-#define S5P_CIWDOFST_CLROVFIY (1 << 30)
-#define S5P_CIWDOFST_CLROVRLB (1 << 29)
-#define S5P_CIWDOFST_HOROFF_MASK (0x7ff << 16)
-#define S5P_CIWDOFST_CLROVFICB (1 << 15)
-#define S5P_CIWDOFST_CLROVFICR (1 << 14)
-#define S5P_CIWDOFST_HOROFF(x) ((x) << 16)
-#define S5P_CIWDOFST_VEROFF(x) ((x) << 0)
-#define S5P_CIWDOFST_VEROFF_MASK (0xfff << 0)
-
-/* Global control */
-#define S5P_CIGCTRL 0x08
-#define S5P_CIGCTRL_SWRST (1 << 31)
-#define S5P_CIGCTRL_CAMRST_A (1 << 30)
-#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29)
-#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27)
-#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27)
-#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27)
-#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27)
-#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27)
-#define S5P_CIGCTRL_TESTPAT_SHIFT (27)
-#define S5P_CIGCTRL_INVPOLPCLK (1 << 26)
-#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25)
-#define S5P_CIGCTRL_INVPOLHREF (1 << 24)
-#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22)
-#define S5P_CIGCTRL_HREF_MASK (1 << 21)
-#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20)
-#define S5P_CIGCTRL_IRQ_CLR (1 << 19)
-#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16)
-#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12)
-#define S5P_CIGCTRL_CAM_JPEG (1 << 8)
-#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7)
-#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6)
-/* 0 - ITU601; 1 - ITU709 */
-#define S5P_CIGCTRL_CSC_ITU601_709 (1 << 5)
-#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4)
-#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3)
-#define S5P_CIGCTRL_INVPOLFIELD (1 << 1)
-#define S5P_CIGCTRL_INTERLACE (1 << 0)
-
-/* Window offset 2 */
-#define S5P_CIWDOFST2 0x14
-#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16)
-#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0)
-#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16)
-#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0)
-
-/* Output DMA Y/Cb/Cr plane start addresses */
-#define S5P_CIOYSA(n) (0x18 + (n) * 4)
-#define S5P_CIOCBSA(n) (0x28 + (n) * 4)
-#define S5P_CIOCRSA(n) (0x38 + (n) * 4)
-
-/* Target image format */
-#define S5P_CITRGFMT 0x48
-#define S5P_CITRGFMT_INROT90 (1 << 31)
-#define S5P_CITRGFMT_YCBCR420 (0 << 29)
-#define S5P_CITRGFMT_YCBCR422 (1 << 29)
-#define S5P_CITRGFMT_YCBCR422_1P (2 << 29)
-#define S5P_CITRGFMT_RGB (3 << 29)
-#define S5P_CITRGFMT_FMT_MASK (3 << 29)
-#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16)
-#define S5P_CITRGFMT_FLIP_SHIFT (14)
-#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14)
-#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14)
-#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14)
-#define S5P_CITRGFMT_FLIP_180 (3 << 14)
-#define S5P_CITRGFMT_FLIP_MASK (3 << 14)
-#define S5P_CITRGFMT_OUTROT90 (1 << 13)
-#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0)
-#define S5P_CITRGFMT_HSIZE(x) ((x) << 16)
-#define S5P_CITRGFMT_VSIZE(x) ((x) << 0)
-
-/* Output DMA control */
-#define S5P_CIOCTRL 0x4c
-#define S5P_CIOCTRL_ORDER422_MASK (3 << 0)
-#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0)
-#define S5P_CIOCTRL_ORDER422_CBYCRY (1 << 0)
-#define S5P_CIOCTRL_ORDER422_YCRYCB (2 << 0)
-#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0)
-#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2)
-#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3)
-#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3)
-#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3)
-#define S5P_CIOCTRL_ALPHA_OUT_MASK (0xff << 4)
-#define S5P_CIOCTRL_RGB16FMT_MASK (3 << 16)
-#define S5P_CIOCTRL_RGB565 (0 << 16)
-#define S5P_CIOCTRL_ARGB1555 (1 << 16)
-#define S5P_CIOCTRL_ARGB4444 (2 << 16)
-#define S5P_CIOCTRL_ORDER2P_SHIFT (24)
-#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24)
-#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24)
-
-/* Pre-scaler control 1 */
-#define S5P_CISCPRERATIO 0x50
-#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28)
-#define S5P_CISCPRERATIO_HOR(x) ((x) << 16)
-#define S5P_CISCPRERATIO_VER(x) ((x) << 0)
-
-#define S5P_CISCPREDST 0x54
-#define S5P_CISCPREDST_WIDTH(x) ((x) << 16)
-#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0)
-
-/* Main scaler control */
-#define S5P_CISCCTRL 0x58
-#define S5P_CISCCTRL_SCALERBYPASS (1 << 31)
-#define S5P_CISCCTRL_SCALEUP_H (1 << 30)
-#define S5P_CISCCTRL_SCALEUP_V (1 << 29)
-#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28)
-#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27)
-#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26)
-#define S5P_CISCCTRL_INTERLACE (1 << 25)
-#define S5P_CISCCTRL_SCALERSTART (1 << 15)
-#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13)
-#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13)
-#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13)
-#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13)
-#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11)
-#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11)
-#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11)
-#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11)
-#define S5P_CISCCTRL_RGB_EXT (1 << 10)
-#define S5P_CISCCTRL_ONE2ONE (1 << 9)
-#define S5P_CISCCTRL_MHRATIO(x) ((x) << 16)
-#define S5P_CISCCTRL_MVRATIO(x) ((x) << 0)
-#define S5P_CISCCTRL_MHRATIO_MASK (0x1ff << 16)
-#define S5P_CISCCTRL_MVRATIO_MASK (0x1ff << 0)
-#define S5P_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16)
-#define S5P_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0)
-
-/* Target area */
-#define S5P_CITAREA 0x5c
-#define S5P_CITAREA_MASK 0x0fffffff
-
-/* General status */
-#define S5P_CISTATUS 0x64
-#define S5P_CISTATUS_OVFIY (1 << 31)
-#define S5P_CISTATUS_OVFICB (1 << 30)
-#define S5P_CISTATUS_OVFICR (1 << 29)
-#define S5P_CISTATUS_VSYNC (1 << 28)
-#define S5P_CISTATUS_FRAMECNT_MASK (3 << 26)
-#define S5P_CISTATUS_FRAMECNT_SHIFT 26
-#define S5P_CISTATUS_WINOFF_EN (1 << 25)
-#define S5P_CISTATUS_IMGCPT_EN (1 << 22)
-#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21)
-#define S5P_CISTATUS_VSYNC_A (1 << 20)
-#define S5P_CISTATUS_VSYNC_B (1 << 19)
-#define S5P_CISTATUS_OVRLB (1 << 18)
-#define S5P_CISTATUS_FRAME_END (1 << 17)
-#define S5P_CISTATUS_LASTCAPT_END (1 << 16)
-#define S5P_CISTATUS_VVALID_A (1 << 15)
-#define S5P_CISTATUS_VVALID_B (1 << 14)
-
-/* Indexes to the last and the currently processed buffer. */
-#define S5P_CISTATUS2 0x68
-
-/* Image capture control */
-#define S5P_CIIMGCPT 0xc0
-#define S5P_CIIMGCPT_IMGCPTEN (1 << 31)
-#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30)
-#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25)
-#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18)
-
-/* Frame capture sequence */
-#define S5P_CICPTSEQ 0xc4
-
-/* Image effect */
-#define S5P_CIIMGEFF 0xd0
-#define S5P_CIIMGEFF_IE_ENABLE (1 << 30)
-#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29)
-#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29)
-#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26)
-#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26)
-#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26)
-#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26)
-#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26)
-#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26)
-#define S5P_CIIMGEFF_FIN_MASK (7 << 26)
-#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0))
-#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13)
-#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0)
-
-/* Input DMA Y/Cb/Cr plane start address 0/1 */
-#define S5P_CIIYSA(n) (0xd4 + (n) * 0x70)
-#define S5P_CIICBSA(n) (0xd8 + (n) * 0x70)
-#define S5P_CIICRSA(n) (0xdc + (n) * 0x70)
-
-/* Real input DMA image size */
-#define S5P_CIREAL_ISIZE 0xf8
-#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31)
-#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30)
-#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16)
-#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0)
-
-
-/* Input DMA control */
-#define S5P_MSCTRL 0xfc
-#define S5P_MSCTRL_IN_BURST_COUNT_MASK (0xF << 24)
-#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16)
-#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16
-#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15)
-#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15)
-#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15)
-#define S5P_MSCTRL_FLIP_SHIFT 13
-#define S5P_MSCTRL_FLIP_MASK (3 << 13)
-#define S5P_MSCTRL_FLIP_NORMAL (0 << 13)
-#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13)
-#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13)
-#define S5P_MSCTRL_FLIP_180 (3 << 13)
-#define S5P_MSCTRL_FIFO_CTRL_FULL (1 << 12)
-#define S5P_MSCTRL_ORDER422_SHIFT 4
-#define S5P_MSCTRL_ORDER422_YCBYCR (0 << 4)
-#define S5P_MSCTRL_ORDER422_CBYCRY (1 << 4)
-#define S5P_MSCTRL_ORDER422_YCRYCB (2 << 4)
-#define S5P_MSCTRL_ORDER422_CRYCBY (3 << 4)
-#define S5P_MSCTRL_ORDER422_MASK (3 << 4)
-#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3)
-#define S5P_MSCTRL_INPUT_MEMORY (1 << 3)
-#define S5P_MSCTRL_INPUT_MASK (1 << 3)
-#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1)
-#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1)
-#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1)
-#define S5P_MSCTRL_INFORMAT_RGB (3 << 1)
-#define S5P_MSCTRL_INFORMAT_MASK (3 << 1)
-#define S5P_MSCTRL_ENVID (1 << 0)
-#define S5P_MSCTRL_IN_BURST_COUNT(x) ((x) << 24)
-
-/* Output DMA Y/Cb/Cr offset */
-#define S5P_CIOYOFF 0x168
-#define S5P_CIOCBOFF 0x16c
-#define S5P_CIOCROFF 0x170
-
-/* Input DMA Y/Cb/Cr offset */
-#define S5P_CIIYOFF 0x174
-#define S5P_CIICBOFF 0x178
-#define S5P_CIICROFF 0x17c
-
-#define S5P_CIO_OFFS_VER(x) ((x) << 16)
-#define S5P_CIO_OFFS_HOR(x) ((x) << 0)
-
-/* Input DMA original image size */
-#define S5P_ORGISIZE 0x180
-
-/* Output DMA original image size */
-#define S5P_ORGOSIZE 0x184
-
-#define S5P_ORIG_SIZE_VER(x) ((x) << 16)
-#define S5P_ORIG_SIZE_HOR(x) ((x) << 0)
-
-/* Real output DMA image size (extension register) */
-#define S5P_CIEXTEN 0x188
-#define S5P_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10)
-#define S5P_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f)
-#define S5P_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10)
-#define S5P_CIEXTEN_MVRATIO_EXT_MASK 0x3f
-
-#define S5P_CIDMAPARAM 0x18c
-#define S5P_CIDMAPARAM_R_LINEAR (0 << 29)
-#define S5P_CIDMAPARAM_R_64X32 (3 << 29)
-#define S5P_CIDMAPARAM_W_LINEAR (0 << 13)
-#define S5P_CIDMAPARAM_W_64X32 (3 << 13)
-#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13))
-
-/* MIPI CSI image format */
-#define S5P_CSIIMGFMT 0x194
-#define S5P_CSIIMGFMT_YCBCR422_8BIT 0x1e
-#define S5P_CSIIMGFMT_RAW8 0x2a
-#define S5P_CSIIMGFMT_RAW10 0x2b
-#define S5P_CSIIMGFMT_RAW12 0x2c
-/* User defined formats. x = 0...16. */
-#define S5P_CSIIMGFMT_USER(x) (0x30 + x - 1)
-
-/* Output frame buffer sequence mask */
-#define S5P_CIFCNTSEQ 0x1FC
-
-#endif /* REGS_FIMC_H_ */
diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c
index 789de74014e..7c98ee7377e 100644
--- a/drivers/media/video/s5p-g2d/g2d.c
+++ b/drivers/media/video/s5p-g2d/g2d.c
@@ -65,7 +65,7 @@ static struct g2d_fmt formats[] = {
};
#define NUM_FORMATS ARRAY_SIZE(formats)
-struct g2d_frame def_frame = {
+static struct g2d_frame def_frame = {
.width = DEFAULT_WIDTH,
.height = DEFAULT_HEIGHT,
.c_width = DEFAULT_WIDTH,
@@ -77,7 +77,7 @@ struct g2d_frame def_frame = {
.bottom = DEFAULT_HEIGHT,
};
-struct g2d_fmt *find_fmt(struct v4l2_format *f)
+static struct g2d_fmt *find_fmt(struct v4l2_format *f)
{
unsigned int i;
for (i = 0; i < NUM_FORMATS; i++) {
@@ -202,7 +202,7 @@ static const struct v4l2_ctrl_ops g2d_ctrl_ops = {
.s_ctrl = g2d_s_ctrl,
};
-int g2d_setup_ctrls(struct g2d_ctx *ctx)
+static int g2d_setup_ctrls(struct g2d_ctx *ctx)
{
struct g2d_dev *dev = ctx->dev;
@@ -546,11 +546,11 @@ static void job_abort(void *prv)
struct g2d_dev *dev = ctx->dev;
int ret;
- if (dev->curr == 0) /* No job currently running */
+ if (dev->curr == NULL) /* No job currently running */
return;
ret = wait_event_timeout(dev->irq_queue,
- dev->curr == 0,
+ dev->curr == NULL,
msecs_to_jiffies(G2D_TIMEOUT));
}
@@ -599,19 +599,19 @@ static irqreturn_t g2d_isr(int irq, void *prv)
g2d_clear_int(dev);
clk_disable(dev->gate);
- BUG_ON(ctx == 0);
+ BUG_ON(ctx == NULL);
src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
- BUG_ON(src == 0);
- BUG_ON(dst == 0);
+ BUG_ON(src == NULL);
+ BUG_ON(dst == NULL);
v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx);
- dev->curr = 0;
+ dev->curr = NULL;
wake_up(&dev->irq_queue);
return IRQ_HANDLED;
}
@@ -674,42 +674,27 @@ static int g2d_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
+
spin_lock_init(&dev->ctrl_lock);
mutex_init(&dev->mutex);
atomic_set(&dev->num_inst, 0);
init_waitqueue_head(&dev->irq_queue);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to find registers\n");
- ret = -ENOENT;
- goto free_dev;
- }
- dev->res_regs = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
-
- if (!dev->res_regs) {
- dev_err(&pdev->dev, "failed to obtain register region\n");
- ret = -ENOENT;
- goto free_dev;
- }
-
- dev->regs = ioremap(res->start, resource_size(res));
- if (!dev->regs) {
- dev_err(&pdev->dev, "failed to map registers\n");
- ret = -ENOENT;
- goto rel_res_regs;
+ dev->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (dev->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
}
dev->clk = clk_get(&pdev->dev, "sclk_fimg2d");
if (IS_ERR_OR_NULL(dev->clk)) {
dev_err(&pdev->dev, "failed to get g2d clock\n");
- ret = -ENXIO;
- goto unmap_regs;
+ return -ENXIO;
}
ret = clk_prepare(dev->clk);
@@ -740,7 +725,8 @@ static int g2d_probe(struct platform_device *pdev)
dev->irq = res->start;
- ret = request_irq(dev->irq, g2d_isr, 0, pdev->name, dev);
+ ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr,
+ 0, pdev->name, dev);
if (ret) {
dev_err(&pdev->dev, "failed to install IRQ\n");
goto put_clk_gate;
@@ -749,7 +735,7 @@ static int g2d_probe(struct platform_device *pdev)
dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(dev->alloc_ctx)) {
ret = PTR_ERR(dev->alloc_ctx);
- goto rel_irq;
+ goto unprep_clk_gate;
}
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
@@ -762,6 +748,10 @@ static int g2d_probe(struct platform_device *pdev)
goto unreg_v4l2_dev;
}
*vfd = g2d_videodev;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
vfd->lock = &dev->mutex;
ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
if (ret) {
@@ -793,8 +783,6 @@ unreg_v4l2_dev:
v4l2_device_unregister(&dev->v4l2_dev);
alloc_ctx_cleanup:
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
-rel_irq:
- free_irq(dev->irq, dev);
unprep_clk_gate:
clk_unprepare(dev->gate);
put_clk_gate:
@@ -803,12 +791,7 @@ unprep_clk:
clk_unprepare(dev->clk);
put_clk:
clk_put(dev->clk);
-unmap_regs:
- iounmap(dev->regs);
-rel_res_regs:
- release_resource(dev->res_regs);
-free_dev:
- kfree(dev);
+
return ret;
}
@@ -821,14 +804,10 @@ static int g2d_remove(struct platform_device *pdev)
video_unregister_device(dev->vfd);
v4l2_device_unregister(&dev->v4l2_dev);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
- free_irq(dev->irq, dev);
clk_unprepare(dev->gate);
clk_put(dev->gate);
clk_unprepare(dev->clk);
clk_put(dev->clk);
- iounmap(dev->regs);
- release_resource(dev->res_regs);
- kfree(dev);
return 0;
}
diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/video/s5p-g2d/g2d.h
index 1b82065aeae..6b765b0216c 100644
--- a/drivers/media/video/s5p-g2d/g2d.h
+++ b/drivers/media/video/s5p-g2d/g2d.h
@@ -23,7 +23,6 @@ struct g2d_dev {
spinlock_t ctrl_lock;
atomic_t num_inst;
struct vb2_alloc_ctx *alloc_ctx;
- struct resource *res_regs;
void __iomem *regs;
struct clk *clk;
struct clk *gate;
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c
index 5a49c307f9c..28b5225d94f 100644
--- a/drivers/media/video/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/video/s5p-jpeg/jpeg-core.c
@@ -813,7 +813,7 @@ static int s5p_jpeg_streamoff(struct file *file, void *priv,
return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
}
-int s5p_jpeg_g_selection(struct file *file, void *priv,
+static int s5p_jpeg_g_selection(struct file *file, void *priv,
struct v4l2_selection *s)
{
struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
@@ -1290,7 +1290,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
int ret;
/* JPEG IP abstraction struct */
- jpeg = kzalloc(sizeof(struct s5p_jpeg), GFP_KERNEL);
+ jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL);
if (!jpeg)
return -ENOMEM;
@@ -1300,43 +1300,25 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
/* memory-mapped registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "cannot find IO resource\n");
- ret = -ENOENT;
- goto jpeg_alloc_rollback;
- }
-
- jpeg->ioarea = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (!jpeg->ioarea) {
- dev_err(&pdev->dev, "cannot request IO\n");
- ret = -ENXIO;
- goto jpeg_alloc_rollback;
- }
- jpeg->regs = ioremap(res->start, resource_size(res));
- if (!jpeg->regs) {
- dev_err(&pdev->dev, "cannot map IO\n");
- ret = -ENXIO;
- goto mem_region_rollback;
+ jpeg->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (jpeg->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
}
- dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
- jpeg->regs, jpeg->ioarea, res);
-
/* interrupt service routine registration */
jpeg->irq = ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
- goto ioremap_rollback;
+ return ret;
}
- ret = request_irq(jpeg->irq, s5p_jpeg_irq, 0,
- dev_name(&pdev->dev), jpeg);
-
+ ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0,
+ dev_name(&pdev->dev), jpeg);
if (ret) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq);
- goto ioremap_rollback;
+ return ret;
}
/* clocks */
@@ -1344,7 +1326,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
if (IS_ERR(jpeg->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = PTR_ERR(jpeg->clk);
- goto request_irq_rollback;
+ return ret;
}
dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk);
clk_enable(jpeg->clk);
@@ -1386,6 +1368,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
jpeg->vfd_encoder->release = video_device_release;
jpeg->vfd_encoder->lock = &jpeg->lock;
jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_encoder->flags);
ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1);
if (ret) {
@@ -1413,6 +1399,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
jpeg->vfd_decoder->release = video_device_release;
jpeg->vfd_decoder->lock = &jpeg->lock;
jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev;
+ /* Locking in file operations other than ioctl should be done by the driver,
+ not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_decoder->flags);
ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
if (ret) {
@@ -1456,18 +1446,6 @@ clk_get_rollback:
clk_disable(jpeg->clk);
clk_put(jpeg->clk);
-request_irq_rollback:
- free_irq(jpeg->irq, jpeg);
-
-ioremap_rollback:
- iounmap(jpeg->regs);
-
-mem_region_rollback:
- release_resource(jpeg->ioarea);
- release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea));
-
-jpeg_alloc_rollback:
- kfree(jpeg);
return ret;
}
@@ -1488,14 +1466,6 @@ static int s5p_jpeg_remove(struct platform_device *pdev)
clk_disable(jpeg->clk);
clk_put(jpeg->clk);
- free_irq(jpeg->irq, jpeg);
-
- iounmap(jpeg->regs);
-
- release_resource(jpeg->ioarea);
- release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea));
- kfree(jpeg);
-
return 0;
}
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h
index 38d7367f7a6..9d0cd2b76f6 100644
--- a/drivers/media/video/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/video/s5p-jpeg/jpeg-core.h
@@ -54,7 +54,6 @@
* @vfd_encoder: video device node for encoder mem2mem mode
* @vfd_decoder: video device node for decoder mem2mem mode
* @m2m_dev: v4l2 mem2mem device data
- * @ioarea: JPEG IP memory region
* @regs: JPEG IP registers mapping
* @irq: JPEG IP irq
* @clk: JPEG IP clock
@@ -70,7 +69,6 @@ struct s5p_jpeg {
struct video_device *vfd_decoder;
struct v4l2_m2m_dev *m2m_dev;
- struct resource *ioarea;
void __iomem *regs;
unsigned int irq;
struct clk *clk;
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c
index 83fe461af26..9bb68e7b5ae 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc.c
@@ -70,7 +70,7 @@ static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason,
wake_up(&dev->queue);
}
-void s5p_mfc_watchdog(unsigned long arg)
+static void s5p_mfc_watchdog(unsigned long arg)
{
struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg;
@@ -373,7 +373,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx,
/* If no context is available then all necessary
* processing has been done. */
- if (ctx == 0)
+ if (ctx == NULL)
return;
dev = ctx->dev;
@@ -429,7 +429,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev;
unsigned int guard_width, guard_height;
- if (ctx == 0)
+ if (ctx == NULL)
return;
dev = ctx->dev;
if (ctx->c_ops->post_seq_start) {
@@ -496,7 +496,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev;
unsigned long flags;
- if (ctx == 0)
+ if (ctx == NULL)
return;
dev = ctx->dev;
s5p_mfc_clear_int_flags(dev);
@@ -772,7 +772,7 @@ err_queue_init:
err_init_hw:
s5p_mfc_release_firmware(dev);
err_alloc_fw:
- dev->ctx[ctx->num] = 0;
+ dev->ctx[ctx->num] = NULL;
del_timer_sync(&dev->watchdog_timer);
s5p_mfc_clock_off();
err_pwr_enable:
@@ -849,7 +849,7 @@ static int s5p_mfc_release(struct file *file)
}
mfc_debug(2, "Shutting down clock\n");
s5p_mfc_clock_off();
- dev->ctx[ctx->num] = 0;
+ dev->ctx[ctx->num] = NULL;
s5p_mfc_dec_ctrls_delete(ctx);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
@@ -948,7 +948,7 @@ static int s5p_mfc_probe(struct platform_device *pdev)
int ret;
pr_debug("%s++\n", __func__);
- dev = kzalloc(sizeof *dev, GFP_KERNEL);
+ dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL);
if (!dev) {
dev_err(&pdev->dev, "Not enough memory for MFC device\n");
return -ENOMEM;
@@ -959,49 +959,35 @@ static int s5p_mfc_probe(struct platform_device *pdev)
dev->plat_dev = pdev;
if (!dev->plat_dev) {
dev_err(&pdev->dev, "No platform data specified\n");
- ret = -ENODEV;
- goto err_dev;
+ return -ENODEV;
}
ret = s5p_mfc_init_pm(dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed to get mfc clock source\n");
- goto err_clk;
+ return ret;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory region resource\n");
- ret = -ENOENT;
- goto err_res;
- }
- dev->mfc_mem = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (dev->mfc_mem == NULL) {
- dev_err(&pdev->dev, "failed to get memory region\n");
- ret = -ENOENT;
- goto err_mem_reg;
- }
- dev->regs_base = ioremap(dev->mfc_mem->start, resource_size(dev->mfc_mem));
+ dev->regs_base = devm_request_and_ioremap(&pdev->dev, res);
if (dev->regs_base == NULL) {
- dev_err(&pdev->dev, "failed to ioremap address region\n");
- ret = -ENOENT;
- goto err_ioremap;
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get irq resource\n");
ret = -ENOENT;
- goto err_get_res;
+ goto err_res;
}
dev->irq = res->start;
- ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name,
- dev);
+ ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
+ IRQF_DISABLED, pdev->name, dev);
if (ret) {
dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret);
- goto err_req_irq;
+ goto err_res;
}
dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l",
@@ -1009,20 +995,20 @@ static int s5p_mfc_probe(struct platform_device *pdev)
if (!dev->mem_dev_l) {
mfc_err("Mem child (L) device get failed\n");
ret = -ENODEV;
- goto err_find_child;
+ goto err_res;
}
dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r",
match_child);
if (!dev->mem_dev_r) {
mfc_err("Mem child (R) device get failed\n");
ret = -ENODEV;
- goto err_find_child;
+ goto err_res;
}
dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l);
if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) {
ret = PTR_ERR(dev->alloc_ctx[0]);
- goto err_mem_init_ctx_0;
+ goto err_res;
}
dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r);
if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) {
@@ -1048,6 +1034,10 @@ static int s5p_mfc_probe(struct platform_device *pdev)
vfd->ioctl_ops = get_dec_v4l2_ioctl_ops();
vfd->release = video_device_release,
vfd->lock = &dev->mfc_mutex;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
vfd->v4l2_dev = &dev->v4l2_dev;
snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME);
dev->vfd_dec = vfd;
@@ -1072,6 +1062,8 @@ static int s5p_mfc_probe(struct platform_device *pdev)
vfd->ioctl_ops = get_enc_v4l2_ioctl_ops();
vfd->release = video_device_release,
vfd->lock = &dev->mfc_mutex;
+ /* This should not be necessary */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
vfd->v4l2_dev = &dev->v4l2_dev;
snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME);
dev->vfd_enc = vfd;
@@ -1110,22 +1102,9 @@ err_v4l2_dev_reg:
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
err_mem_init_ctx_1:
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
-err_mem_init_ctx_0:
-err_find_child:
- free_irq(dev->irq, dev);
-err_req_irq:
-err_get_res:
- iounmap(dev->regs_base);
- dev->regs_base = NULL;
-err_ioremap:
- release_resource(dev->mfc_mem);
- kfree(dev->mfc_mem);
-err_mem_reg:
err_res:
s5p_mfc_final_pm(dev);
-err_clk:
-err_dev:
- kfree(dev);
+
pr_debug("%s-- with error\n", __func__);
return ret;
@@ -1148,15 +1127,7 @@ static int __devexit s5p_mfc_remove(struct platform_device *pdev)
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]);
- free_irq(dev->irq, dev);
- iounmap(dev->regs_base);
- if (dev->mfc_mem) {
- release_resource(dev->mfc_mem);
- kfree(dev->mfc_mem);
- dev->mfc_mem = NULL;
- }
s5p_mfc_final_pm(dev);
- kfree(dev);
return 0;
}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
index 91146fa622e..bd5706a6bad 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h
@@ -185,7 +185,6 @@ struct s5p_mfc_pm {
* @mem_dev_r: child device of the right memory bank (1)
* @regs_base: base address of the MFC hw registers
* @irq: irq resource
- * @mfc_mem: MFC registers memory resource
* @dec_ctrl_handler: control framework handler for decoding
* @enc_ctrl_handler: control framework handler for encoding
* @pm: power management control
@@ -221,7 +220,6 @@ struct s5p_mfc_dev {
struct device *mem_dev_r;
void __iomem *regs_base;
int irq;
- struct resource *mfc_mem;
struct v4l2_ctrl_handler dec_ctrl_handler;
struct v4l2_ctrl_handler enc_ctrl_handler;
struct s5p_mfc_pm pm;
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
index f2481a85e0a..08a5cfeaa59 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c
@@ -52,7 +52,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc(
dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size);
if (IS_ERR(s5p_mfc_bitproc_buf)) {
- s5p_mfc_bitproc_buf = 0;
+ s5p_mfc_bitproc_buf = NULL;
mfc_err("Allocating bitprocessor buffer failed\n");
release_firmware(fw_blob);
return -ENOMEM;
@@ -63,7 +63,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
mfc_err("The base memory for bank 1 is not aligned to 128KB\n");
vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = 0;
+ s5p_mfc_bitproc_buf = NULL;
release_firmware(fw_blob);
return -EIO;
}
@@ -72,7 +72,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
mfc_err("Bitprocessor memory remap failed\n");
vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = 0;
+ s5p_mfc_bitproc_buf = NULL;
release_firmware(fw_blob);
return -EIO;
}
@@ -82,7 +82,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
if (IS_ERR(b_base)) {
vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = 0;
+ s5p_mfc_bitproc_buf = NULL;
mfc_err("Allocating bank2 base failed\n");
release_firmware(fw_blob);
return -ENOMEM;
@@ -94,7 +94,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev)
mfc_err("The base memory for bank 2 is not aligned to 128KB\n");
vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = 0;
+ s5p_mfc_bitproc_buf = NULL;
release_firmware(fw_blob);
return -EIO;
}
@@ -126,7 +126,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev)
release_firmware(fw_blob);
return -ENOMEM;
}
- if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) {
+ if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) {
mfc_err("MFC firmware is not allocated or was not mapped correctly\n");
release_firmware(fw_blob);
return -EINVAL;
@@ -146,9 +146,9 @@ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev)
if (!s5p_mfc_bitproc_buf)
return -EINVAL;
vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf);
- s5p_mfc_bitproc_virt = 0;
+ s5p_mfc_bitproc_virt = NULL;
s5p_mfc_bitproc_phys = 0;
- s5p_mfc_bitproc_buf = 0;
+ s5p_mfc_bitproc_buf = NULL;
return 0;
}
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
index dff9dc79879..acedb2004be 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c
@@ -1436,7 +1436,8 @@ static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = {
.s_ctrl = s5p_mfc_enc_s_ctrl,
};
-int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+static int vidioc_s_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
@@ -1452,7 +1453,8 @@ int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
return 0;
}
-int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *a)
+static int vidioc_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *a)
{
struct s5p_mfc_ctx *ctx = fh_to_ctx(priv);
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
index e08b21c50eb..e6217cbfa4a 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c
@@ -43,7 +43,7 @@ int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx)
ctx->desc_buf = vb2_dma_contig_memops.alloc(
dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE);
if (IS_ERR_VALUE((int)ctx->desc_buf)) {
- ctx->desc_buf = 0;
+ ctx->desc_buf = NULL;
mfc_err("Allocating DESC buffer failed\n");
return -ENOMEM;
}
@@ -54,7 +54,7 @@ int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx)
if (desc_virt == NULL) {
vb2_dma_contig_memops.put(ctx->desc_buf);
ctx->desc_phys = 0;
- ctx->desc_buf = 0;
+ ctx->desc_buf = NULL;
mfc_err("Remapping DESC buffer failed\n");
return -ENOMEM;
}
@@ -69,7 +69,7 @@ void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
if (ctx->desc_phys) {
vb2_dma_contig_memops.put(ctx->desc_buf);
ctx->desc_phys = 0;
- ctx->desc_buf = 0;
+ ctx->desc_buf = NULL;
}
}
@@ -186,7 +186,7 @@ int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
ctx->bank1_buf = vb2_dma_contig_memops.alloc(
dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size);
if (IS_ERR(ctx->bank1_buf)) {
- ctx->bank1_buf = 0;
+ ctx->bank1_buf = NULL;
printk(KERN_ERR
"Buf alloc for decoding failed (port A)\n");
return -ENOMEM;
@@ -200,7 +200,7 @@ int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx)
ctx->bank2_buf = vb2_dma_contig_memops.alloc(
dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size);
if (IS_ERR(ctx->bank2_buf)) {
- ctx->bank2_buf = 0;
+ ctx->bank2_buf = NULL;
mfc_err("Buf alloc for decoding failed (port B)\n");
return -ENOMEM;
}
@@ -216,13 +216,13 @@ void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx)
{
if (ctx->bank1_buf) {
vb2_dma_contig_memops.put(ctx->bank1_buf);
- ctx->bank1_buf = 0;
+ ctx->bank1_buf = NULL;
ctx->bank1_phys = 0;
ctx->bank1_size = 0;
}
if (ctx->bank2_buf) {
vb2_dma_contig_memops.put(ctx->bank2_buf);
- ctx->bank2_buf = 0;
+ ctx->bank2_buf = NULL;
ctx->bank2_phys = 0;
ctx->bank2_size = 0;
}
@@ -244,7 +244,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
if (IS_ERR(ctx->ctx_buf)) {
mfc_err("Allocating context buffer failed\n");
ctx->ctx_phys = 0;
- ctx->ctx_buf = 0;
+ ctx->ctx_buf = NULL;
return -ENOMEM;
}
ctx->ctx_phys = s5p_mfc_mem_cookie(
@@ -256,7 +256,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
mfc_err("Remapping instance buffer failed\n");
vb2_dma_contig_memops.put(ctx->ctx_buf);
ctx->ctx_phys = 0;
- ctx->ctx_buf = 0;
+ ctx->ctx_buf = NULL;
return -ENOMEM;
}
/* Zero content of the allocated memory */
@@ -265,7 +265,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx)
if (s5p_mfc_init_shm(ctx) < 0) {
vb2_dma_contig_memops.put(ctx->ctx_buf);
ctx->ctx_phys = 0;
- ctx->ctx_buf = 0;
+ ctx->ctx_buf = NULL;
return -ENOMEM;
}
return 0;
@@ -277,12 +277,12 @@ void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx)
if (ctx->ctx_buf) {
vb2_dma_contig_memops.put(ctx->ctx_buf);
ctx->ctx_phys = 0;
- ctx->ctx_buf = 0;
+ ctx->ctx_buf = NULL;
}
if (ctx->shm_alloc) {
vb2_dma_contig_memops.put(ctx->shm_alloc);
- ctx->shm_alloc = 0;
- ctx->shm = 0;
+ ctx->shm_alloc = NULL;
+ ctx->shm = NULL;
}
}
@@ -296,7 +296,7 @@ void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx)
}
/* Set registers for shared buffer */
-void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
+static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx)
{
struct s5p_mfc_dev *dev = ctx->dev;
mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR);
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
index 4865d25a0e5..20cb6eef297 100644
--- a/drivers/media/video/s5p-tv/hdmi_drv.c
+++ b/drivers/media/video/s5p-tv/hdmi_drv.c
@@ -42,7 +42,23 @@ MODULE_DESCRIPTION("Samsung HDMI");
MODULE_LICENSE("GPL");
/* default preset configured on probe */
-#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60
+#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94
+
+struct hdmi_pulse {
+ u32 beg;
+ u32 end;
+};
+
+struct hdmi_timings {
+ struct hdmi_pulse hact;
+ u32 hsyn_pol; /* 0 - high, 1 - low */
+ struct hdmi_pulse hsyn;
+ u32 interlaced;
+ struct hdmi_pulse vact[2];
+ u32 vsyn_pol; /* 0 - high, 1 - low */
+ u32 vsyn_off;
+ struct hdmi_pulse vsyn[2];
+};
struct hdmi_resources {
struct clk *hdmi;
@@ -70,64 +86,15 @@ struct hdmi_device {
/** subdev of MHL interface */
struct v4l2_subdev *mhl_sd;
/** configuration of current graphic mode */
- const struct hdmi_preset_conf *cur_conf;
+ const struct hdmi_timings *cur_conf;
+ /** flag indicating that timings are dirty */
+ int cur_conf_dirty;
/** current preset */
u32 cur_preset;
/** other resources */
struct hdmi_resources res;
};
-struct hdmi_tg_regs {
- u8 cmd;
- u8 h_fsz_l;
- u8 h_fsz_h;
- u8 hact_st_l;
- u8 hact_st_h;
- u8 hact_sz_l;
- u8 hact_sz_h;
- u8 v_fsz_l;
- u8 v_fsz_h;
- u8 vsync_l;
- u8 vsync_h;
- u8 vsync2_l;
- u8 vsync2_h;
- u8 vact_st_l;
- u8 vact_st_h;
- u8 vact_sz_l;
- u8 vact_sz_h;
- u8 field_chg_l;
- u8 field_chg_h;
- u8 vact_st2_l;
- u8 vact_st2_h;
- u8 vsync_top_hdmi_l;
- u8 vsync_top_hdmi_h;
- u8 vsync_bot_hdmi_l;
- u8 vsync_bot_hdmi_h;
- u8 field_top_hdmi_l;
- u8 field_top_hdmi_h;
- u8 field_bot_hdmi_l;
- u8 field_bot_hdmi_h;
-};
-
-struct hdmi_core_regs {
- u8 h_blank[2];
- u8 v_blank[3];
- u8 h_v_line[3];
- u8 vsync_pol[1];
- u8 int_pro_mode[1];
- u8 v_blank_f[3];
- u8 h_sync_gen[3];
- u8 v_sync_gen1[3];
- u8 v_sync_gen2[3];
- u8 v_sync_gen3[3];
-};
-
-struct hdmi_preset_conf {
- struct hdmi_core_regs core;
- struct hdmi_tg_regs tg;
- struct v4l2_mbus_framefmt mbus_fmt;
-};
-
static struct platform_device_id hdmi_driver_types[] = {
{
.name = "s5pv210-hdmi",
@@ -165,6 +132,21 @@ void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value)
writeb(value, hdev->regs + reg_id);
}
+static inline
+void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value)
+{
+ switch (n) {
+ default:
+ writeb(value >> 24, hdev->regs + reg_id + 12);
+ case 3:
+ writeb(value >> 16, hdev->regs + reg_id + 8);
+ case 2:
+ writeb(value >> 8, hdev->regs + reg_id + 4);
+ case 1:
+ writeb(value >> 0, hdev->regs + reg_id + 0);
+ }
+}
+
static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id)
{
return readl(hdev->regs + reg_id);
@@ -211,77 +193,72 @@ static void hdmi_reg_init(struct hdmi_device *hdev)
}
static void hdmi_timing_apply(struct hdmi_device *hdev,
- const struct hdmi_preset_conf *conf)
+ const struct hdmi_timings *t)
{
- const struct hdmi_core_regs *core = &conf->core;
- const struct hdmi_tg_regs *tg = &conf->tg;
-
/* setting core registers */
- hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]);
- hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]);
- hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]);
- hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]);
- hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]);
- hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]);
- hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]);
- hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]);
- hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]);
- hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
- hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
- hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
- hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
- hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
- hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
- hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
- hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+ hdmi_writebn(hdev, HDMI_H_BLANK_0, 2, t->hact.beg);
+ hdmi_writebn(hdev, HDMI_H_SYNC_GEN_0, 3,
+ (t->hsyn_pol << 20) | (t->hsyn.end << 10) | t->hsyn.beg);
+ hdmi_writeb(hdev, HDMI_VSYNC_POL, t->vsyn_pol);
+ hdmi_writebn(hdev, HDMI_V_BLANK_0, 3,
+ (t->vact[0].beg << 11) | t->vact[0].end);
+ hdmi_writebn(hdev, HDMI_V_SYNC_GEN_1_0, 3,
+ (t->vsyn[0].beg << 12) | t->vsyn[0].end);
+ if (t->interlaced) {
+ u32 vsyn_trans = t->hsyn.beg + t->vsyn_off;
+
+ hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 1);
+ hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3,
+ (t->hact.end << 12) | t->vact[1].end);
+ hdmi_writebn(hdev, HDMI_V_BLANK_F_0, 3,
+ (t->vact[1].end << 11) | t->vact[1].beg);
+ hdmi_writebn(hdev, HDMI_V_SYNC_GEN_2_0, 3,
+ (t->vsyn[1].beg << 12) | t->vsyn[1].end);
+ hdmi_writebn(hdev, HDMI_V_SYNC_GEN_3_0, 3,
+ (vsyn_trans << 12) | vsyn_trans);
+ } else {
+ hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 0);
+ hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3,
+ (t->hact.end << 12) | t->vact[0].end);
+ }
+
/* Timing generator registers */
- hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
- hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
- hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l);
- hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h);
- hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
- hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
- hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
- hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
- hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l);
- hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h);
- hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l);
- hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h);
- hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l);
- hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h);
- hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
- hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
- hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
- hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
- hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
- hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
- hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
- hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
- hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
- hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
- hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
- hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
- hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
- hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+ hdmi_writebn(hdev, HDMI_TG_H_FSZ_L, 2, t->hact.end);
+ hdmi_writebn(hdev, HDMI_TG_HACT_ST_L, 2, t->hact.beg);
+ hdmi_writebn(hdev, HDMI_TG_HACT_SZ_L, 2, t->hact.end - t->hact.beg);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC_L, 2, t->vsyn[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_VACT_ST_L, 2, t->vact[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_VACT_SZ_L, 2,
+ t->vact[0].end - t->vact[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, 2, t->vsyn[0].beg);
+ hdmi_writebn(hdev, HDMI_TG_FIELD_TOP_HDMI_L, 2, t->vsyn[0].beg);
+ if (t->interlaced) {
+ hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_FIELD_EN);
+ hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[1].end);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC2_L, 2, t->vsyn[1].beg);
+ hdmi_writebn(hdev, HDMI_TG_FIELD_CHG_L, 2, t->vact[0].end);
+ hdmi_writebn(hdev, HDMI_TG_VACT_ST2_L, 2, t->vact[1].beg);
+ hdmi_writebn(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, 2, t->vsyn[1].beg);
+ hdmi_writebn(hdev, HDMI_TG_FIELD_BOT_HDMI_L, 2, t->vsyn[1].beg);
+ } else {
+ hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_FIELD_EN);
+ hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[0].end);
+ }
}
static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
{
struct device *dev = hdmi_dev->dev;
- const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf;
+ const struct hdmi_timings *conf = hdmi_dev->cur_conf;
struct v4l2_dv_preset preset;
int ret;
dev_dbg(dev, "%s\n", __func__);
+ /* skip if conf is already synchronized with HW */
+ if (!hdmi_dev->cur_conf_dirty)
+ return 0;
+
/* reset hdmiphy */
hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
mdelay(10);
@@ -307,6 +284,8 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev)
/* setting core registers */
hdmi_timing_apply(hdmi_dev, conf);
+ hdmi_dev->cur_conf_dirty = 0;
+
return 0;
}
@@ -398,156 +377,126 @@ static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix)
#undef DUMPREG
}
-static const struct hdmi_preset_conf hdmi_conf_480p = {
- .core = {
- .h_blank = {0x8a, 0x00},
- .v_blank = {0x0d, 0x6a, 0x01},
- .h_v_line = {0x0d, 0xa2, 0x35},
- .vsync_pol = {0x01},
- .int_pro_mode = {0x00},
- .v_blank_f = {0x00, 0x00, 0x00},
- .h_sync_gen = {0x0e, 0x30, 0x11},
- .v_sync_gen1 = {0x0f, 0x90, 0x00},
- /* other don't care */
- },
- .tg = {
- 0x00, /* cmd */
- 0x5a, 0x03, /* h_fsz */
- 0x8a, 0x00, 0xd0, 0x02, /* hact */
- 0x0d, 0x02, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x2d, 0x00, 0xe0, 0x01, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x49, 0x02, /* vact_st2 */
- 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- },
- .mbus_fmt = {
- .width = 720,
- .height = 480,
- .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
+static const struct hdmi_timings hdmi_timings_480p = {
+ .hact = { .beg = 138, .end = 858 },
+ .hsyn_pol = 1,
+ .hsyn = { .beg = 16, .end = 16 + 62 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 42 + 3, .end = 522 + 3 },
+ .vsyn_pol = 1,
+ .vsyn[0] = { .beg = 6 + 3, .end = 12 + 3},
};
-static const struct hdmi_preset_conf hdmi_conf_720p60 = {
- .core = {
- .h_blank = {0x72, 0x01},
- .v_blank = {0xee, 0xf2, 0x00},
- .h_v_line = {0xee, 0x22, 0x67},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x00},
- .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
- .h_sync_gen = {0x6c, 0x50, 0x02},
- .v_sync_gen1 = {0x0a, 0x50, 0x00},
- /* other don't care */
- },
- .tg = {
- 0x00, /* cmd */
- 0x72, 0x06, /* h_fsz */
- 0x72, 0x01, 0x00, 0x05, /* hact */
- 0xee, 0x02, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x1e, 0x00, 0xd0, 0x02, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x49, 0x02, /* vact_st2 */
- 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- },
- .mbus_fmt = {
- .width = 1280,
- .height = 720,
- .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
+static const struct hdmi_timings hdmi_timings_576p50 = {
+ .hact = { .beg = 144, .end = 864 },
+ .hsyn_pol = 1,
+ .hsyn = { .beg = 12, .end = 12 + 64 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 44 + 5, .end = 620 + 5 },
+ .vsyn_pol = 1,
+ .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
};
-static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
- .core = {
- .h_blank = {0xd0, 0x02},
- .v_blank = {0x65, 0x6c, 0x01},
- .h_v_line = {0x65, 0x04, 0xa5},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x00},
- .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
- .h_sync_gen = {0x0e, 0xea, 0x08},
- .v_sync_gen1 = {0x09, 0x40, 0x00},
- /* other don't care */
- },
- .tg = {
- 0x00, /* cmd */
- 0x98, 0x08, /* h_fsz */
- 0x18, 0x01, 0x80, 0x07, /* hact */
- 0x65, 0x04, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x2d, 0x00, 0x38, 0x04, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x49, 0x02, /* vact_st2 */
- 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- },
- .mbus_fmt = {
- .width = 1920,
- .height = 1080,
- .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
+static const struct hdmi_timings hdmi_timings_720p60 = {
+ .hact = { .beg = 370, .end = 1650 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 110, .end = 110 + 40 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 25 + 5, .end = 745 + 5 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
};
-static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
- .core = {
- .h_blank = {0x18, 0x01},
- .v_blank = {0x65, 0x6c, 0x01},
- .h_v_line = {0x65, 0x84, 0x89},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x00},
- .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */
- .h_sync_gen = {0x56, 0x08, 0x02},
- .v_sync_gen1 = {0x09, 0x40, 0x00},
- /* other don't care */
- },
- .tg = {
- 0x00, /* cmd */
- 0x98, 0x08, /* h_fsz */
- 0x18, 0x01, 0x80, 0x07, /* hact */
- 0x65, 0x04, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x2d, 0x00, 0x38, 0x04, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x48, 0x02, /* vact_st2 */
- 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- },
- .mbus_fmt = {
- .width = 1920,
- .height = 1080,
- .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- },
+static const struct hdmi_timings hdmi_timings_720p50 = {
+ .hact = { .beg = 700, .end = 1980 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 440, .end = 440 + 40 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 25 + 5, .end = 745 + 5 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5},
+};
+
+static const struct hdmi_timings hdmi_timings_1080p24 = {
+ .hact = { .beg = 830, .end = 2750 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 638, .end = 638 + 44 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
+};
+
+static const struct hdmi_timings hdmi_timings_1080p60 = {
+ .hact = { .beg = 280, .end = 2200 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 88, .end = 88 + 44 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
+};
+
+static const struct hdmi_timings hdmi_timings_1080i60 = {
+ .hact = { .beg = 280, .end = 2200 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 88, .end = 88 + 44 },
+ .interlaced = 1,
+ .vact[0] = { .beg = 20 + 2, .end = 560 + 2 },
+ .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 },
+ .vsyn_pol = 0,
+ .vsyn_off = 1100,
+ .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2},
+ .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2},
+};
+
+static const struct hdmi_timings hdmi_timings_1080i50 = {
+ .hact = { .beg = 720, .end = 2640 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 528, .end = 528 + 44 },
+ .interlaced = 1,
+ .vact[0] = { .beg = 20 + 2, .end = 560 + 2 },
+ .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 },
+ .vsyn_pol = 0,
+ .vsyn_off = 1320,
+ .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2},
+ .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2},
+};
+
+static const struct hdmi_timings hdmi_timings_1080p50 = {
+ .hact = { .beg = 720, .end = 2640 },
+ .hsyn_pol = 0,
+ .hsyn = { .beg = 528, .end = 528 + 44 },
+ .interlaced = 0,
+ .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 },
+ .vsyn_pol = 0,
+ .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4},
};
static const struct {
u32 preset;
- const struct hdmi_preset_conf *conf;
-} hdmi_conf[] = {
- { V4L2_DV_480P59_94, &hdmi_conf_480p },
- { V4L2_DV_720P59_94, &hdmi_conf_720p60 },
- { V4L2_DV_1080P50, &hdmi_conf_1080p50 },
- { V4L2_DV_1080P30, &hdmi_conf_1080p60 },
- { V4L2_DV_1080P60, &hdmi_conf_1080p60 },
+ const struct hdmi_timings *timings;
+} hdmi_timings[] = {
+ { V4L2_DV_480P59_94, &hdmi_timings_480p },
+ { V4L2_DV_576P50, &hdmi_timings_576p50 },
+ { V4L2_DV_720P50, &hdmi_timings_720p50 },
+ { V4L2_DV_720P59_94, &hdmi_timings_720p60 },
+ { V4L2_DV_720P60, &hdmi_timings_720p60 },
+ { V4L2_DV_1080P24, &hdmi_timings_1080p24 },
+ { V4L2_DV_1080P30, &hdmi_timings_1080p60 },
+ { V4L2_DV_1080P50, &hdmi_timings_1080p50 },
+ { V4L2_DV_1080I50, &hdmi_timings_1080i50 },
+ { V4L2_DV_1080I60, &hdmi_timings_1080i60 },
+ { V4L2_DV_1080P60, &hdmi_timings_1080p60 },
};
-static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset)
+static const struct hdmi_timings *hdmi_preset2timings(u32 preset)
{
int i;
- for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i)
- if (hdmi_conf[i].preset == preset)
- return hdmi_conf[i].conf;
+ for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i)
+ if (hdmi_timings[i].preset == preset)
+ return hdmi_timings[i].timings;
return NULL;
}
@@ -559,6 +508,10 @@ static int hdmi_streamon(struct hdmi_device *hdev)
dev_dbg(dev, "%s\n", __func__);
+ ret = hdmi_conf_apply(hdev);
+ if (ret)
+ return ret;
+
ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1);
if (ret)
return ret;
@@ -671,14 +624,15 @@ static int hdmi_s_dv_preset(struct v4l2_subdev *sd,
{
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
struct device *dev = hdev->dev;
- const struct hdmi_preset_conf *conf;
+ const struct hdmi_timings *conf;
- conf = hdmi_preset2conf(preset->preset);
+ conf = hdmi_preset2timings(preset->preset);
if (conf == NULL) {
dev_err(dev, "preset (%u) not supported\n", preset->preset);
return -EINVAL;
}
hdev->cur_conf = conf;
+ hdev->cur_conf_dirty = 1;
hdev->cur_preset = preset->preset;
return 0;
}
@@ -695,21 +649,32 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)
{
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
- struct device *dev = hdev->dev;
+ const struct hdmi_timings *t = hdev->cur_conf;
- dev_dbg(dev, "%s\n", __func__);
+ dev_dbg(hdev->dev, "%s\n", __func__);
if (!hdev->cur_conf)
return -EINVAL;
- *fmt = hdev->cur_conf->mbus_fmt;
+ memset(fmt, 0, sizeof *fmt);
+ fmt->width = t->hact.end - t->hact.beg;
+ fmt->height = t->vact[0].end - t->vact[0].beg;
+ fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ if (t->interlaced) {
+ fmt->field = V4L2_FIELD_INTERLACED;
+ fmt->height *= 2;
+ } else {
+ fmt->field = V4L2_FIELD_NONE;
+ }
return 0;
}
static int hdmi_enum_dv_presets(struct v4l2_subdev *sd,
struct v4l2_dv_enum_preset *preset)
{
- if (preset->index >= ARRAY_SIZE(hdmi_conf))
+ if (preset->index >= ARRAY_SIZE(hdmi_timings))
return -EINVAL;
- return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset);
+ return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset,
+ preset);
}
static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = {
@@ -737,6 +702,8 @@ static int hdmi_runtime_suspend(struct device *dev)
dev_dbg(dev, "%s\n", __func__);
v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0);
hdmi_resource_poweroff(&hdev->res);
+ /* flag that device context is lost */
+ hdev->cur_conf_dirty = 1;
return 0;
}
@@ -750,10 +717,6 @@ static int hdmi_runtime_resume(struct device *dev)
hdmi_resource_poweron(&hdev->res);
- ret = hdmi_conf_apply(hdev);
- if (ret)
- goto fail;
-
/* starting MHL */
ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
if (hdev->mhl_sd && ret)
@@ -993,7 +956,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
strlcpy(sd->name, "s5p-hdmi", sizeof sd->name);
hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET;
/* FIXME: missing fail preset is not supported */
- hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset);
+ hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset);
+ hdmi_dev->cur_conf_dirty = 1;
/* storing subdev for call that have only access to struct device */
dev_set_drvdata(dev, sd);
diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c
index 0afef77747e..f67b3863180 100644
--- a/drivers/media/video/s5p-tv/hdmiphy_drv.c
+++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c
@@ -26,53 +26,188 @@ MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
MODULE_LICENSE("GPL");
struct hdmiphy_conf {
- u32 preset;
+ unsigned long pixclk;
const u8 *data;
};
-static const u8 hdmiphy_conf27[32] = {
- 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
- 0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87,
- 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
- 0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00,
+struct hdmiphy_ctx {
+ struct v4l2_subdev sd;
+ const struct hdmiphy_conf *conf_tab;
};
-static const u8 hdmiphy_conf74_175[32] = {
- 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
- 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
- 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
- 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
+static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = {
+ { .pixclk = 27000000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
+ 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 27027000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
+ 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 74176000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
+ 0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 74250000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
+ 0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA,
+ 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
+ },
+ { /* end marker */ }
};
-static const u8 hdmiphy_conf74_25[32] = {
- 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
- 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
- 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0,
- 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
+static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = {
+ { .pixclk = 27000000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
+ 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 27027000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
+ 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 74176000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
+ 0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 74250000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
+ 0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
+ 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 148352000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
+ 0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
+ 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, }
+ },
+ { .pixclk = 148500000, .data = (u8 [32]) {
+ 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
+ 0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
+ 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
+ 0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, }
+ },
+ { /* end marker */ }
};
-static const u8 hdmiphy_conf148_5[32] = {
- 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
- 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
- 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
- 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
+static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = {
+ { .pixclk = 27000000, .data = (u8 [32]) {
+ 0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08,
+ 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
+ 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
+ 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 27027000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08,
+ 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
+ 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
+ 0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 74176000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08,
+ 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
+ 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
+ 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 74250000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08,
+ 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
+ 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
+ 0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 148500000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08,
+ 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
+ 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4,
+ 0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { /* end marker */ }
};
-static const struct hdmiphy_conf hdmiphy_conf[] = {
- { V4L2_DV_480P59_94, hdmiphy_conf27 },
- { V4L2_DV_1080P30, hdmiphy_conf74_175 },
- { V4L2_DV_720P59_94, hdmiphy_conf74_175 },
- { V4L2_DV_720P60, hdmiphy_conf74_25 },
- { V4L2_DV_1080P50, hdmiphy_conf148_5 },
- { V4L2_DV_1080P60, hdmiphy_conf148_5 },
+static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = {
+ { .pixclk = 27000000, .data = (u8 [32]) {
+ 0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08,
+ 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 27027000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08,
+ 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 74176000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08,
+ 0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 74250000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
+ 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { .pixclk = 148500000, .data = (u8 [32]) {
+ 0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
+ 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
+ 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
+ },
+ { /* end marker */ }
};
-const u8 *hdmiphy_preset2conf(u32 preset)
+static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct hdmiphy_ctx, sd);
+}
+
+static unsigned long hdmiphy_preset_to_pixclk(u32 preset)
+{
+ static const unsigned long pixclk[] = {
+ [V4L2_DV_480P59_94] = 27000000,
+ [V4L2_DV_576P50] = 27000000,
+ [V4L2_DV_720P59_94] = 74176000,
+ [V4L2_DV_720P50] = 74250000,
+ [V4L2_DV_720P60] = 74250000,
+ [V4L2_DV_1080P24] = 74250000,
+ [V4L2_DV_1080P30] = 74250000,
+ [V4L2_DV_1080I50] = 74250000,
+ [V4L2_DV_1080I60] = 74250000,
+ [V4L2_DV_1080P50] = 148500000,
+ [V4L2_DV_1080P60] = 148500000,
+ };
+ if (preset < ARRAY_SIZE(pixclk))
+ return pixclk[preset];
+ else
+ return 0;
+}
+
+static const u8 *hdmiphy_find_conf(u32 preset, const struct hdmiphy_conf *conf)
{
- int i;
- for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i)
- if (hdmiphy_conf[i].preset == preset)
- return hdmiphy_conf[i].data;
+ unsigned long pixclk;
+
+ pixclk = hdmiphy_preset_to_pixclk(preset);
+ if (!pixclk)
+ return NULL;
+
+ for (; conf->pixclk; ++conf)
+ if (conf->pixclk == pixclk)
+ return conf->data;
return NULL;
}
@@ -88,11 +223,12 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
const u8 *data;
u8 buffer[32];
int ret;
+ struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct device *dev = &client->dev;
dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset);
- data = hdmiphy_preset2conf(preset->preset);
+ data = hdmiphy_find_conf(preset->preset, ctx->conf_tab);
if (!data) {
dev_err(dev, "format not supported\n");
return -EINVAL;
@@ -146,21 +282,36 @@ static const struct v4l2_subdev_ops hdmiphy_ops = {
static int __devinit hdmiphy_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- static struct v4l2_subdev sd;
+ struct hdmiphy_ctx *ctx;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data;
+ v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops);
- v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops);
dev_info(&client->dev, "probe successful\n");
return 0;
}
static int __devexit hdmiphy_remove(struct i2c_client *client)
{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
+
+ kfree(ctx);
dev_info(&client->dev, "remove successful\n");
+
return 0;
}
static const struct i2c_device_id hdmiphy_id[] = {
- { "hdmiphy", 0 },
+ { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 },
+ { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 },
+ { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 },
+ { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 },
+ { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 },
{ },
};
MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h
index 1597078c4a5..ddb422e2355 100644
--- a/drivers/media/video/s5p-tv/mixer.h
+++ b/drivers/media/video/s5p-tv/mixer.h
@@ -226,6 +226,7 @@ struct mxr_resources {
/* event flags used */
enum mxr_devide_flags {
MXR_EVENT_VSYNC = 0,
+ MXR_EVENT_TOP = 1,
};
/** drivers instance */
@@ -293,7 +294,7 @@ int __devinit mxr_acquire_video(struct mxr_device *mdev,
struct mxr_output_conf *output_cont, int output_count);
/** releasing common video resources */
-void __devexit mxr_release_video(struct mxr_device *mdev);
+void mxr_release_video(struct mxr_device *mdev);
struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx);
struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx);
diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c
index a2c0c25ad13..edca0659288 100644
--- a/drivers/media/video/s5p-tv/mixer_drv.c
+++ b/drivers/media/video/s5p-tv/mixer_drv.c
@@ -461,7 +461,7 @@ static struct platform_driver mxr_driver __refdata = {
static int __init mxr_init(void)
{
int i, ret;
- static const char banner[] __initdata = KERN_INFO
+ static const char banner[] __initconst = KERN_INFO
"Samsung TV Mixer driver, "
"(c) 2010-2011 Samsung Electronics Co., Ltd.\n";
printk(banner);
diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c
index 4800a3cbb29..3b1670a045f 100644
--- a/drivers/media/video/s5p-tv/mixer_reg.c
+++ b/drivers/media/video/s5p-tv/mixer_reg.c
@@ -296,21 +296,25 @@ irqreturn_t mxr_irq_handler(int irq, void *dev_data)
/* wake up process waiting for VSYNC */
if (val & MXR_INT_STATUS_VSYNC) {
set_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
+ /* toggle TOP field event if working in interlaced mode */
+ if (~mxr_read(mdev, MXR_CFG) & MXR_CFG_SCAN_PROGRASSIVE)
+ change_bit(MXR_EVENT_TOP, &mdev->event_flags);
wake_up(&mdev->event_queue);
- }
-
- /* clear interrupts */
- if (~val & MXR_INT_EN_VSYNC) {
/* vsync interrupt use different bit for read and clear */
- val &= ~MXR_INT_EN_VSYNC;
+ val &= ~MXR_INT_STATUS_VSYNC;
val |= MXR_INT_CLEAR_VSYNC;
}
+
+ /* clear interrupts */
mxr_write(mdev, MXR_INT_STATUS, val);
spin_unlock(&mdev->reg_slock);
/* leave on non-vsync event */
if (~val & MXR_INT_CLEAR_VSYNC)
return IRQ_HANDLED;
+ /* skip layer update on bottom field */
+ if (!test_bit(MXR_EVENT_TOP, &mdev->event_flags))
+ return IRQ_HANDLED;
for (i = 0; i < MXR_MAX_LAYERS; ++i)
mxr_irq_layer_handle(mdev->layer[i]);
return IRQ_HANDLED;
@@ -333,6 +337,7 @@ void mxr_reg_streamon(struct mxr_device *mdev)
/* start MIXER */
mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN);
+ set_bit(MXR_EVENT_TOP, &mdev->event_flags);
spin_unlock_irqrestore(&mdev->reg_slock, flags);
}
diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c
index f7ca5cc143c..33fde2a763e 100644
--- a/drivers/media/video/s5p-tv/mixer_video.c
+++ b/drivers/media/video/s5p-tv/mixer_video.c
@@ -140,7 +140,7 @@ fail:
return ret;
}
-void __devexit mxr_release_video(struct mxr_device *mdev)
+void mxr_release_video(struct mxr_device *mdev)
{
int i;
@@ -853,8 +853,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt,
*nplanes = fmt->num_subframes;
for (i = 0; i < fmt->num_subframes; ++i) {
alloc_ctxs[i] = layer->mdev->alloc_ctx;
- sizes[i] = PAGE_ALIGN(planes[i].sizeimage);
- mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]);
+ sizes[i] = planes[i].sizeimage;
+ mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]);
}
if (*nbuffers == 0)
@@ -1069,6 +1069,10 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev,
set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags);
video_set_drvdata(&layer->vfd, layer);
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &layer->vfd.flags);
layer->vfd.lock = &layer->mutex;
layer->vfd.v4l2_dev = &mdev->v4l2_dev;
diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h
index 33247d13e4c..a889d1f57f2 100644
--- a/drivers/media/video/s5p-tv/regs-hdmi.h
+++ b/drivers/media/video/s5p-tv/regs-hdmi.h
@@ -140,6 +140,7 @@
#define HDMI_MODE_MASK (3 << 0)
/* HDMI_TG_CMD */
+#define HDMI_TG_FIELD_EN (1 << 1)
#define HDMI_TG_EN (1 << 0)
#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 53aae5968ff..bc08f1dbc29 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -5080,6 +5080,36 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x0200000,
},
},
+ [SAA7134_BOARD_ASUSTeK_PS3_100] = {
+ .name = "Asus My Cinema PS3-100",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tuner_config = 2,
+ .gpiomask = 1 << 21,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 0,
+ .amux = LINE2,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0200000,
+ },
+ },
[SAA7134_BOARD_REAL_ANGEL_220] = {
.name = "Zogis Real Angel 220",
.audio_clock = 0x00187de7,
@@ -6877,6 +6907,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1,
}, {
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1043,
+ .subdevice = 0x48cd,
+ .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
.subvendor = 0x17de,
.subdevice = 0x7128,
@@ -7347,6 +7383,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_KWORLD_TERMINATOR:
case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
case SAA7134_BOARD_FLYDVBT_LR301:
+ case SAA7134_BOARD_ASUSTeK_PS3_100:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
@@ -7811,6 +7848,14 @@ int saa7134_board_init2(struct saa7134_dev *dev)
i2c_transfer(&dev->i2c_adap, &msg, 1);
break;
}
+ case SAA7134_BOARD_ASUSTeK_PS3_100:
+ {
+ u8 data[] = { 0x3c, 0x33, 0x60};
+ struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data,
+ .len = sizeof(data)};
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
case SAA7134_BOARD_FLYDVB_TRIO:
{
u8 temp = 0;
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index aaa5c97a721..5dfd826d734 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -881,6 +881,20 @@ static struct tda1004x_config asus_tiger_3in1_config = {
.request_firmware = philips_tda1004x_request_firmware
};
+static struct tda1004x_config asus_ps3_100_config = {
+ .demod_address = 0x0b,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .gpio_config = TDA10046_GP11_I,
+ .if_freq = TDA10046_FREQ_045,
+ .i2c_gate = 0x4b,
+ .tuner_address = 0x61,
+ .antenna_switch = 1,
+ .request_firmware = philips_tda1004x_request_firmware
+};
+
/* ------------------------------------------------------------------
* special case: this card uses saa713x GPIO22 for the mode switch
*/
@@ -1647,6 +1661,31 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap, 0, 0) == NULL) {
wprintk("%s: Asus Tiger 3in1, no lnbp21"
" found!\n", __func__);
+ goto dettach_frontend;
+ }
+ }
+ }
+ break;
+ case SAA7134_BOARD_ASUSTeK_PS3_100:
+ if (!use_frontend) { /* terrestrial */
+ if (configure_tda827x_fe(dev, &asus_ps3_100_config,
+ &tda827x_cfg_2) < 0)
+ goto dettach_frontend;
+ } else { /* satellite */
+ fe0->dvb.frontend = dvb_attach(tda10086_attach,
+ &flydvbs, &dev->i2c_adap);
+ if (fe0->dvb.frontend) {
+ if (dvb_attach(tda826x_attach,
+ fe0->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ wprintk("%s: Asus My Cinema PS3-100, no "
+ "tda826x found!\n", __func__);
+ goto dettach_frontend;
+ }
+ if (dvb_attach(lnbp21_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0, 0) == NULL) {
+ wprintk("%s: Asus My Cinema PS3-100, no lnbp21"
+ " found!\n", __func__);
goto dettach_frontend;
}
}
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 48d2878699b..05c6e217d8a 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -753,6 +753,13 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keycode = 0xffff;
raw_decode = true;
break;
+ case SAA7134_BOARD_ASUSTeK_PS3_100:
+ ir_codes = RC_MAP_ASUS_PS3_100;
+ mask_keydown = 0x0040000;
+ mask_keyup = 0x0040000;
+ mask_keycode = 0xffff;
+ raw_decode = true;
+ break;
case SAA7134_BOARD_ENCORE_ENLTV:
case SAA7134_BOARD_ENCORE_ENLTV_FM:
ir_codes = RC_MAP_ENCORE_ENLTV;
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 417034eb6ad..6de10b1e725 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -2036,7 +2036,7 @@ static int saa7134_s_tuner(struct file *file, void *priv,
mode = dev->thread.mode;
if (UNSET == mode) {
rx = saa7134_tvaudio_getstereo(dev);
- mode = saa7134_tvaudio_rx2mode(t->rxsubchans);
+ mode = saa7134_tvaudio_rx2mode(rx);
}
if (mode != t->audmode)
dev->thread.mode = t->audmode;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index f625060e6a0..89c8333736a 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -332,6 +332,7 @@ struct saa7134_card_ir {
#define SAA7134_BOARD_BEHOLD_503FM 187
#define SAA7134_BOARD_SENSORAY811_911 188
#define SAA7134_BOARD_KWORLD_PC150U 189
+#define SAA7134_BOARD_ASUSTeK_PS3_100 190
#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c
index 273cf807401..d8e6c8f1407 100644
--- a/drivers/media/video/saa7164/saa7164-vbi.c
+++ b/drivers/media/video/saa7164/saa7164-vbi.c
@@ -952,7 +952,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port)
/* Stop the hardware, regardless */
result = saa7164_vbi_stop_port(port);
- if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ if (result != SAA_OK) {
printk(KERN_ERR "%s() pause/forced stop transition "
"failed, res = 0x%x\n", __func__, result);
}
@@ -971,7 +971,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port)
/* Stop the hardware, regardless */
result = saa7164_vbi_acquire_port(port);
result = saa7164_vbi_stop_port(port);
- if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
+ if (result != SAA_OK) {
printk(KERN_ERR "%s() run/forced stop transition "
"failed, res = 0x%x\n", __func__, result);
}
diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h
index 742b34103b5..8d120e3baf7 100644
--- a/drivers/media/video/saa7164/saa7164.h
+++ b/drivers/media/video/saa7164/saa7164.h
@@ -611,11 +611,6 @@ extern unsigned int saa_debug;
printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\
} while (0)
-#define log_err(fmt, arg...)\
- do { \
- printk(KERN_ERROR "%s: " fmt, dev->name, ## arg);\
- } while (0)
-
#define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2))
#define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2))
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index 424dfacd263..0baaf94db7e 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -210,27 +210,33 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq,
struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int bytes_per_line;
- unsigned int height;
if (fmt) {
const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd,
fmt->fmt.pix.pixelformat);
+ unsigned int bytes_per_line;
+ int ret;
+
if (!xlate)
return -EINVAL;
- bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
- xlate->host_fmt);
- height = fmt->fmt.pix.height;
+
+ ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+
+ ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+ fmt->fmt.pix.height);
+ if (ret < 0)
+ return ret;
+
+ sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
} else {
/* Called from VIDIOC_REQBUFS or in compatibility mode */
- bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- height = icd->user_height;
+ sizes[0] = icd->sizeimage;
}
- if (bytes_per_line < 0)
- return bytes_per_line;
-
- sizes[0] = bytes_per_line * height;
alloc_ctxs[0] = pcdev->alloc_ctx;
@@ -336,21 +342,15 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
ceu_write(pcdev, top1, phys_addr_top);
if (V4L2_FIELD_NONE != pcdev->field) {
- if (planar)
- phys_addr_bottom = phys_addr_top + icd->user_width;
- else
- phys_addr_bottom = phys_addr_top +
- soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
+ phys_addr_bottom = phys_addr_top + icd->bytesperline;
ceu_write(pcdev, bottom1, phys_addr_bottom);
}
if (planar) {
- phys_addr_top += icd->user_width *
- icd->user_height;
+ phys_addr_top += icd->bytesperline * icd->user_height;
ceu_write(pcdev, top2, phys_addr_top);
if (V4L2_FIELD_NONE != pcdev->field) {
- phys_addr_bottom = phys_addr_top + icd->user_width;
+ phys_addr_bottom = phys_addr_top + icd->bytesperline;
ceu_write(pcdev, bottom2, phys_addr_bottom);
}
}
@@ -377,13 +377,8 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb)
struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb);
unsigned long size;
- int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
- icd->current_fmt->host_fmt);
- if (bytes_per_line < 0)
- goto error;
-
- size = icd->user_height * bytes_per_line;
+ size = icd->sizeimage;
if (vb2_plane_size(vb, 0) < size) {
dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
@@ -682,10 +677,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
in_width *= 2;
left_offset *= 2;
}
- cdwdr_width = width;
} else {
- int bytes_per_line = soc_mbus_bytes_per_line(width,
- icd->current_fmt->host_fmt);
unsigned int w_factor;
switch (icd->current_fmt->host_fmt->packing) {
@@ -698,13 +690,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd)
in_width = cam->width * w_factor;
left_offset *= w_factor;
-
- if (bytes_per_line < 0)
- cdwdr_width = width;
- else
- cdwdr_width = bytes_per_line;
}
+ cdwdr_width = icd->bytesperline;
+
height = icd->user_height;
in_height = cam->height;
if (V4L2_FIELD_NONE != pcdev->field) {
@@ -881,11 +870,13 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd)
value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
- value |= pcdev->is_16bit ? 1 << 12 : 0;
- /* CSI2 mode */
- if (pcdev->pdata->csi2)
+ if (pcdev->pdata->csi2) /* CSI2 mode */
value |= 3 << 12;
+ else if (pcdev->is_16bit)
+ value |= 1 << 12;
+ else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT)
+ value |= 2 << 12;
ceu_write(pcdev, CAMCR, value);
@@ -964,24 +955,28 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
}, {
.fourcc = V4L2_PIX_FMT_NV21,
.name = "NV21",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C,
}, {
.fourcc = V4L2_PIX_FMT_NV16,
.name = "NV16",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
}, {
.fourcc = V4L2_PIX_FMT_NV61,
.name = "NV61",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
},
};
@@ -1845,6 +1840,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
return 0;
}
+#define CEU_CHDW_MAX 8188U /* Maximum line stride */
+
static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
@@ -1863,8 +1860,12 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(icd->parent, "Format %x not found\n", pixfmt);
- return -EINVAL;
+ xlate = icd->current_fmt;
+ dev_dbg(icd->parent, "Format %x not found, keeping %x\n",
+ pixfmt, xlate->host_fmt->fourcc);
+ pixfmt = xlate->host_fmt->fourcc;
+ pix->pixelformat = pixfmt;
+ pix->colorspace = icd->colorspace;
}
/* FIXME: calculate using depth and bus width */
@@ -1923,10 +1924,20 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
pix->width = width;
if (mf.height > height)
pix->height = height;
+
+ pix->bytesperline = max(pix->bytesperline, pix->width);
+ pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX);
+ pix->bytesperline &= ~3;
+ break;
+
+ default:
+ /* Configurable stride isn't supported in pass-through mode. */
+ pix->bytesperline = 0;
}
pix->width &= ~3;
pix->height &= ~3;
+ pix->sizeimage = 0;
dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n",
__func__, ret, pix->pixelformat, pix->width, pix->height);
@@ -2145,6 +2156,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
pcdev->ici.nr = pdev->id;
pcdev->ici.drv_name = dev_name(&pdev->dev);
pcdev->ici.ops = &sh_mobile_ceu_host_ops;
+ pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE;
pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(pcdev->alloc_ctx)) {
diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c
index 9644bd861ab..8fd1874382c 100644
--- a/drivers/media/video/sh_vou.c
+++ b/drivers/media/video/sh_vou.c
@@ -1390,6 +1390,10 @@ static int __devinit sh_vou_probe(struct platform_device *pdev)
vdev->v4l2_dev = &vou_dev->v4l2_dev;
vdev->release = video_device_release;
vdev->lock = &vou_dev->fop_lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
vou_dev->vdev = vdev;
video_set_drvdata(vdev, vou_dev);
diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c
new file mode 100644
index 00000000000..a2e41a21dc6
--- /dev/null
+++ b/drivers/media/video/smiapp-pll.c
@@ -0,0 +1,418 @@
+/*
+ * drivers/media/video/smiapp-pll.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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/gcd.h>
+#include <linux/lcm.h>
+#include <linux/module.h>
+
+#include "smiapp-pll.h"
+
+/* Return an even number or one. */
+static inline uint32_t clk_div_even(uint32_t a)
+{
+ return max_t(uint32_t, 1, a & ~1);
+}
+
+/* Return an even number or one. */
+static inline uint32_t clk_div_even_up(uint32_t a)
+{
+ if (a == 1)
+ return 1;
+ return (a + 1) & ~1;
+}
+
+static inline uint32_t is_one_or_even(uint32_t a)
+{
+ if (a == 1)
+ return 1;
+ if (a & 1)
+ return 0;
+
+ return 1;
+}
+
+static int bounds_check(struct device *dev, uint32_t val,
+ uint32_t min, uint32_t max, char *str)
+{
+ if (val >= min && val <= max)
+ return 0;
+
+ dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max);
+
+ return -EINVAL;
+}
+
+static void print_pll(struct device *dev, struct smiapp_pll *pll)
+{
+ dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div);
+ dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier);
+ if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+ dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div);
+ dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div);
+ }
+ dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div);
+ dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div);
+
+ dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz);
+ dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz);
+ dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz);
+ if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) {
+ dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n",
+ pll->op_sys_clk_freq_hz);
+ dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n",
+ pll->op_pix_clk_freq_hz);
+ }
+ dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz);
+ dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz);
+}
+
+int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
+ struct smiapp_pll *pll)
+{
+ uint32_t sys_div;
+ uint32_t best_pix_div = INT_MAX >> 1;
+ uint32_t vt_op_binning_div;
+ uint32_t lane_op_clock_ratio;
+ uint32_t mul, div;
+ uint32_t more_mul_min, more_mul_max;
+ uint32_t more_mul_factor;
+ uint32_t min_vt_div, max_vt_div, vt_div;
+ uint32_t min_sys_div, max_sys_div;
+ unsigned int i;
+ int rval;
+
+ if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE)
+ lane_op_clock_ratio = pll->lanes;
+ else
+ lane_op_clock_ratio = 1;
+ dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio);
+
+ dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal,
+ pll->binning_vertical);
+
+ /* CSI transfers 2 bits per clock per lane; thus times 2 */
+ pll->pll_op_clk_freq_hz = pll->link_freq * 2
+ * (pll->lanes / lane_op_clock_ratio);
+
+ /* Figure out limits for pre-pll divider based on extclk */
+ dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n",
+ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
+ limits->max_pre_pll_clk_div =
+ min_t(uint16_t, limits->max_pre_pll_clk_div,
+ clk_div_even(pll->ext_clk_freq_hz /
+ limits->min_pll_ip_freq_hz));
+ limits->min_pre_pll_clk_div =
+ max_t(uint16_t, limits->min_pre_pll_clk_div,
+ clk_div_even_up(
+ DIV_ROUND_UP(pll->ext_clk_freq_hz,
+ limits->max_pll_ip_freq_hz)));
+ dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n",
+ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
+
+ i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz);
+ mul = div_u64(pll->pll_op_clk_freq_hz, i);
+ div = pll->ext_clk_freq_hz / i;
+ dev_dbg(dev, "mul %d / div %d\n", mul, div);
+
+ limits->min_pre_pll_clk_div =
+ max_t(uint16_t, limits->min_pre_pll_clk_div,
+ clk_div_even_up(
+ DIV_ROUND_UP(mul * pll->ext_clk_freq_hz,
+ limits->max_pll_op_freq_hz)));
+ dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n",
+ limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div);
+
+ if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) {
+ dev_err(dev, "unable to compute pre_pll divisor\n");
+ return -EINVAL;
+ }
+
+ pll->pre_pll_clk_div = limits->min_pre_pll_clk_div;
+
+ /*
+ * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be
+ * too high.
+ */
+ dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div);
+
+ /* Don't go above max pll multiplier. */
+ more_mul_max = limits->max_pll_multiplier / mul;
+ dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n",
+ more_mul_max);
+ /* Don't go above max pll op frequency. */
+ more_mul_max =
+ min_t(int,
+ more_mul_max,
+ limits->max_pll_op_freq_hz
+ / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul));
+ dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n",
+ more_mul_max);
+ /* Don't go above the division capability of op sys clock divider. */
+ more_mul_max = min(more_mul_max,
+ limits->max_op_sys_clk_div * pll->pre_pll_clk_div
+ / div);
+ dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n",
+ more_mul_max);
+ /* Ensure we won't go above min_pll_multiplier. */
+ more_mul_max = min(more_mul_max,
+ DIV_ROUND_UP(limits->max_pll_multiplier, mul));
+ dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n",
+ more_mul_max);
+
+ /* Ensure we won't go below min_pll_op_freq_hz. */
+ more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz,
+ pll->ext_clk_freq_hz / pll->pre_pll_clk_div
+ * mul);
+ dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n",
+ more_mul_min);
+ /* Ensure we won't go below min_pll_multiplier. */
+ more_mul_min = max(more_mul_min,
+ DIV_ROUND_UP(limits->min_pll_multiplier, mul));
+ dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n",
+ more_mul_min);
+
+ if (more_mul_min > more_mul_max) {
+ dev_warn(dev,
+ "unable to compute more_mul_min and more_mul_max");
+ return -EINVAL;
+ }
+
+ more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div;
+ dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor);
+ more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div);
+ dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n",
+ more_mul_factor);
+ i = roundup(more_mul_min, more_mul_factor);
+ if (!is_one_or_even(i))
+ i <<= 1;
+
+ dev_dbg(dev, "final more_mul: %d\n", i);
+ if (i > more_mul_max) {
+ dev_warn(dev, "final more_mul is bad, max %d", more_mul_max);
+ return -EINVAL;
+ }
+
+ pll->pll_multiplier = mul * i;
+ pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div;
+ dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div);
+
+ pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz
+ / pll->pre_pll_clk_div;
+
+ pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz
+ * pll->pll_multiplier;
+
+ /* Derive pll_op_clk_freq_hz. */
+ pll->op_sys_clk_freq_hz =
+ pll->pll_op_clk_freq_hz / pll->op_sys_clk_div;
+
+ pll->op_pix_clk_div = pll->bits_per_pixel;
+ dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div);
+
+ pll->op_pix_clk_freq_hz =
+ pll->op_sys_clk_freq_hz / pll->op_pix_clk_div;
+
+ /*
+ * Some sensors perform analogue binning and some do this
+ * digitally. The ones doing this digitally can be roughly be
+ * found out using this formula. The ones doing this digitally
+ * should run at higher clock rate, so smaller divisor is used
+ * on video timing side.
+ */
+ if (limits->min_line_length_pck_bin > limits->min_line_length_pck
+ / pll->binning_horizontal)
+ vt_op_binning_div = pll->binning_horizontal;
+ else
+ vt_op_binning_div = 1;
+ dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div);
+
+ /*
+ * Profile 2 supports vt_pix_clk_div E [4, 10]
+ *
+ * Horizontal binning can be used as a base for difference in
+ * divisors. One must make sure that horizontal blanking is
+ * enough to accommodate the CSI-2 sync codes.
+ *
+ * Take scaling factor into account as well.
+ *
+ * Find absolute limits for the factor of vt divider.
+ */
+ dev_dbg(dev, "scale_m: %d\n", pll->scale_m);
+ min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div
+ * pll->scale_n,
+ lane_op_clock_ratio * vt_op_binning_div
+ * pll->scale_m);
+
+ /* Find smallest and biggest allowed vt divisor. */
+ dev_dbg(dev, "min_vt_div: %d\n", min_vt_div);
+ min_vt_div = max(min_vt_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->max_vt_pix_clk_freq_hz));
+ dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n",
+ min_vt_div);
+ min_vt_div = max_t(uint32_t, min_vt_div,
+ limits->min_vt_pix_clk_div
+ * limits->min_vt_sys_clk_div);
+ dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div);
+
+ max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div;
+ dev_dbg(dev, "max_vt_div: %d\n", max_vt_div);
+ max_vt_div = min(max_vt_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->min_vt_pix_clk_freq_hz));
+ dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n",
+ max_vt_div);
+
+ /*
+ * Find limitsits for sys_clk_div. Not all values are possible
+ * with all values of pix_clk_div.
+ */
+ min_sys_div = limits->min_vt_sys_clk_div;
+ dev_dbg(dev, "min_sys_div: %d\n", min_sys_div);
+ min_sys_div = max(min_sys_div,
+ DIV_ROUND_UP(min_vt_div,
+ limits->max_vt_pix_clk_div));
+ dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div);
+ min_sys_div = max(min_sys_div,
+ pll->pll_op_clk_freq_hz
+ / limits->max_vt_sys_clk_freq_hz);
+ dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div);
+ min_sys_div = clk_div_even_up(min_sys_div);
+ dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div);
+
+ max_sys_div = limits->max_vt_sys_clk_div;
+ dev_dbg(dev, "max_sys_div: %d\n", max_sys_div);
+ max_sys_div = min(max_sys_div,
+ DIV_ROUND_UP(max_vt_div,
+ limits->min_vt_pix_clk_div));
+ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div);
+ max_sys_div = min(max_sys_div,
+ DIV_ROUND_UP(pll->pll_op_clk_freq_hz,
+ limits->min_vt_pix_clk_freq_hz));
+ dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div);
+
+ /*
+ * Find pix_div such that a legal pix_div * sys_div results
+ * into a value which is not smaller than div, the desired
+ * divisor.
+ */
+ for (vt_div = min_vt_div; vt_div <= max_vt_div;
+ vt_div += 2 - (vt_div & 1)) {
+ for (sys_div = min_sys_div;
+ sys_div <= max_sys_div;
+ sys_div += 2 - (sys_div & 1)) {
+ int pix_div = DIV_ROUND_UP(vt_div, sys_div);
+
+ if (pix_div < limits->min_vt_pix_clk_div
+ || pix_div > limits->max_vt_pix_clk_div) {
+ dev_dbg(dev,
+ "pix_div %d too small or too big (%d--%d)\n",
+ pix_div,
+ limits->min_vt_pix_clk_div,
+ limits->max_vt_pix_clk_div);
+ continue;
+ }
+
+ /* Check if this one is better. */
+ if (pix_div * sys_div
+ <= roundup(min_vt_div, best_pix_div))
+ best_pix_div = pix_div;
+ }
+ if (best_pix_div < INT_MAX >> 1)
+ break;
+ }
+
+ pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div);
+ pll->vt_pix_clk_div = best_pix_div;
+
+ pll->vt_sys_clk_freq_hz =
+ pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div;
+ pll->vt_pix_clk_freq_hz =
+ pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div;
+
+ pll->pixel_rate_csi =
+ pll->op_pix_clk_freq_hz * lane_op_clock_ratio;
+
+ print_pll(dev, pll);
+
+ rval = bounds_check(dev, pll->pre_pll_clk_div,
+ limits->min_pre_pll_clk_div,
+ limits->max_pre_pll_clk_div, "pre_pll_clk_div");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->pll_ip_clk_freq_hz,
+ limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz,
+ "pll_ip_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->pll_multiplier,
+ limits->min_pll_multiplier, limits->max_pll_multiplier,
+ "pll_multiplier");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->pll_op_clk_freq_hz,
+ limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz,
+ "pll_op_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_sys_clk_div,
+ limits->min_op_sys_clk_div, limits->max_op_sys_clk_div,
+ "op_sys_clk_div");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_pix_clk_div,
+ limits->min_op_pix_clk_div, limits->max_op_pix_clk_div,
+ "op_pix_clk_div");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_sys_clk_freq_hz,
+ limits->min_op_sys_clk_freq_hz,
+ limits->max_op_sys_clk_freq_hz,
+ "op_sys_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->op_pix_clk_freq_hz,
+ limits->min_op_pix_clk_freq_hz,
+ limits->max_op_pix_clk_freq_hz,
+ "op_pix_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->vt_sys_clk_freq_hz,
+ limits->min_vt_sys_clk_freq_hz,
+ limits->max_vt_sys_clk_freq_hz,
+ "vt_sys_clk_freq_hz");
+ if (!rval)
+ rval = bounds_check(
+ dev, pll->vt_pix_clk_freq_hz,
+ limits->min_vt_pix_clk_freq_hz,
+ limits->max_vt_pix_clk_freq_hz,
+ "vt_pix_clk_freq_hz");
+
+ return rval;
+}
+EXPORT_SYMBOL_GPL(smiapp_pll_calculate);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
+MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h
new file mode 100644
index 00000000000..9eab63f23af
--- /dev/null
+++ b/drivers/media/video/smiapp-pll.h
@@ -0,0 +1,103 @@
+/*
+ * drivers/media/video/smiapp-pll.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 SMIAPP_PLL_H
+#define SMIAPP_PLL_H
+
+#include <linux/device.h>
+
+struct smiapp_pll {
+ uint8_t lanes;
+ uint8_t binning_horizontal;
+ uint8_t binning_vertical;
+ uint8_t scale_m;
+ uint8_t scale_n;
+ uint8_t bits_per_pixel;
+ uint16_t flags;
+ uint32_t link_freq;
+
+ uint16_t pre_pll_clk_div;
+ uint16_t pll_multiplier;
+ uint16_t op_sys_clk_div;
+ uint16_t op_pix_clk_div;
+ uint16_t vt_sys_clk_div;
+ uint16_t vt_pix_clk_div;
+
+ uint32_t ext_clk_freq_hz;
+ uint32_t pll_ip_clk_freq_hz;
+ uint32_t pll_op_clk_freq_hz;
+ uint32_t op_sys_clk_freq_hz;
+ uint32_t op_pix_clk_freq_hz;
+ uint32_t vt_sys_clk_freq_hz;
+ uint32_t vt_pix_clk_freq_hz;
+
+ uint32_t pixel_rate_csi;
+};
+
+struct smiapp_pll_limits {
+ /* Strict PLL limits */
+ uint32_t min_ext_clk_freq_hz;
+ uint32_t max_ext_clk_freq_hz;
+ uint16_t min_pre_pll_clk_div;
+ uint16_t max_pre_pll_clk_div;
+ uint32_t min_pll_ip_freq_hz;
+ uint32_t max_pll_ip_freq_hz;
+ uint16_t min_pll_multiplier;
+ uint16_t max_pll_multiplier;
+ uint32_t min_pll_op_freq_hz;
+ uint32_t max_pll_op_freq_hz;
+
+ uint16_t min_vt_sys_clk_div;
+ uint16_t max_vt_sys_clk_div;
+ uint32_t min_vt_sys_clk_freq_hz;
+ uint32_t max_vt_sys_clk_freq_hz;
+ uint16_t min_vt_pix_clk_div;
+ uint16_t max_vt_pix_clk_div;
+ uint32_t min_vt_pix_clk_freq_hz;
+ uint32_t max_vt_pix_clk_freq_hz;
+
+ uint16_t min_op_sys_clk_div;
+ uint16_t max_op_sys_clk_div;
+ uint32_t min_op_sys_clk_freq_hz;
+ uint32_t max_op_sys_clk_freq_hz;
+ uint16_t min_op_pix_clk_div;
+ uint16_t max_op_pix_clk_div;
+ uint32_t min_op_pix_clk_freq_hz;
+ uint32_t max_op_pix_clk_freq_hz;
+
+ /* Other relevant limits */
+ uint32_t min_line_length_pck_bin;
+ uint32_t min_line_length_pck;
+};
+
+/* op pix clock is for all lanes in total normally */
+#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
+#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1)
+
+struct device;
+
+int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits,
+ struct smiapp_pll *pll);
+
+#endif /* SMIAPP_PLL_H */
diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig
new file mode 100644
index 00000000000..f7b35ff443b
--- /dev/null
+++ b/drivers/media/video/smiapp/Kconfig
@@ -0,0 +1,6 @@
+config VIDEO_SMIAPP
+ tristate "SMIA++/SMIA sensor support"
+ depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select VIDEO_SMIAPP_PLL
+ ---help---
+ This is a generic driver for SMIA++/SMIA camera modules.
diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile
new file mode 100644
index 00000000000..36b0cfa2c54
--- /dev/null
+++ b/drivers/media/video/smiapp/Makefile
@@ -0,0 +1,5 @@
+smiapp-objs += smiapp-core.o smiapp-regs.o \
+ smiapp-quirk.o smiapp-limits.o
+obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o
+
+ccflags-y += -Idrivers/media/video
diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c
new file mode 100644
index 00000000000..f518026cb67
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-core.c
@@ -0,0 +1,2894 @@
+/*
+ * drivers/media/video/smiapp/smiapp-core.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2010--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * Based on smiapp driver by Vimarsh Zutshi
+ * Based on jt8ev1.c by Vimarsh Zutshi
+ * Based on smia-sensor.c by Tuukka Toivonen <tuukkat76@gmail.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/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-device.h>
+
+#include "smiapp.h"
+
+#define SMIAPP_ALIGN_DIM(dim, flags) \
+ ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \
+ ? ALIGN((dim), 2) \
+ : (dim) & ~1)
+
+/*
+ * smiapp_module_idents - supported camera modules
+ */
+static const struct smiapp_module_ident smiapp_module_idents[] = {
+ SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"),
+ SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"),
+ SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"),
+ SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"),
+ SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"),
+ SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk),
+ SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"),
+ SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"),
+ SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk),
+ SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk),
+ SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk),
+};
+
+/*
+ *
+ * Dynamic Capability Identification
+ *
+ */
+
+static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc;
+ unsigned int i;
+ int rval;
+ int line_count = 0;
+ int embedded_start = -1, embedded_end = -1;
+ int image_start = 0;
+
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE,
+ &fmt_model_type);
+ if (rval)
+ return rval;
+
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE,
+ &fmt_model_subtype);
+ if (rval)
+ return rval;
+
+ ncol_desc = (fmt_model_subtype
+ & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK)
+ >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT;
+ nrow_desc = fmt_model_subtype
+ & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK;
+
+ dev_dbg(&client->dev, "format_model_type %s\n",
+ fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE
+ ? "2 byte" :
+ fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE
+ ? "4 byte" : "is simply bad");
+
+ for (i = 0; i < ncol_desc + nrow_desc; i++) {
+ u32 desc;
+ u32 pixelcode;
+ u32 pixels;
+ char *which;
+ char *what;
+
+ if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i),
+ &desc);
+ if (rval)
+ return rval;
+
+ pixelcode =
+ (desc
+ & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK)
+ >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT;
+ pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK;
+ } else if (fmt_model_type
+ == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i),
+ &desc);
+ if (rval)
+ return rval;
+
+ pixelcode =
+ (desc
+ & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK)
+ >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT;
+ pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK;
+ } else {
+ dev_dbg(&client->dev,
+ "invalid frame format model type %d\n",
+ fmt_model_type);
+ return -EINVAL;
+ }
+
+ if (i < ncol_desc)
+ which = "columns";
+ else
+ which = "rows";
+
+ switch (pixelcode) {
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED:
+ what = "embedded";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY:
+ what = "dummy";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK:
+ what = "black";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK:
+ what = "dark";
+ break;
+ case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE:
+ what = "visible";
+ break;
+ default:
+ what = "invalid";
+ dev_dbg(&client->dev, "pixelcode %d\n", pixelcode);
+ break;
+ }
+
+ dev_dbg(&client->dev, "%s pixels: %d %s\n",
+ what, pixels, which);
+
+ if (i < ncol_desc)
+ continue;
+
+ /* Handle row descriptors */
+ if (pixelcode
+ == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) {
+ embedded_start = line_count;
+ } else {
+ if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE
+ || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2)
+ image_start = line_count;
+ if (embedded_start != -1 && embedded_end == -1)
+ embedded_end = line_count;
+ }
+ line_count += pixels;
+ }
+
+ if (embedded_start == -1 || embedded_end == -1) {
+ embedded_start = 0;
+ embedded_end = 0;
+ }
+
+ dev_dbg(&client->dev, "embedded data from lines %d to %d\n",
+ embedded_start, embedded_end);
+ dev_dbg(&client->dev, "image data starts at line %d\n", image_start);
+
+ return 0;
+}
+
+static int smiapp_pll_configure(struct smiapp_sensor *sensor)
+{
+ struct smiapp_pll *pll = &sensor->pll;
+ int rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier);
+ if (rval < 0)
+ return rval;
+
+ /* Lane op clock ratio does not apply here. */
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS,
+ DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256));
+ if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0)
+ return rval;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div);
+ if (rval < 0)
+ return rval;
+
+ return smiapp_write(
+ sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div);
+}
+
+static int smiapp_pll_update(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct smiapp_pll_limits lim = {
+ .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV],
+ .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV],
+ .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ],
+ .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ],
+ .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER],
+ .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER],
+ .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ],
+ .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ],
+
+ .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV],
+ .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV],
+ .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV],
+ .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV],
+ .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ],
+ .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ],
+ .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ],
+ .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ],
+
+ .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV],
+ .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV],
+ .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV],
+ .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV],
+ .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ],
+ .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ],
+ .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ],
+ .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ],
+
+ .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN],
+ .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK],
+ };
+ struct smiapp_pll *pll = &sensor->pll;
+ int rval;
+
+ memset(&sensor->pll, 0, sizeof(sensor->pll));
+
+ pll->lanes = sensor->platform_data->lanes;
+ pll->ext_clk_freq_hz = sensor->platform_data->ext_clk;
+
+ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) {
+ /*
+ * Fill in operational clock divisors limits from the
+ * video timing ones. On profile 0 sensors the
+ * requirements regarding them are essentially the
+ * same as on VT ones.
+ */
+ lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div;
+ lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div;
+ lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div;
+ lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div;
+ lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz;
+ lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz;
+ lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz;
+ lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz;
+ /* Profile 0 sensors have no separate OP clock branch. */
+ pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS;
+ }
+
+ if (smiapp_needs_quirk(sensor,
+ SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE))
+ pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE;
+
+ pll->binning_horizontal = sensor->binning_horizontal;
+ pll->binning_vertical = sensor->binning_vertical;
+ pll->link_freq =
+ sensor->link_freq->qmenu_int[sensor->link_freq->val];
+ pll->scale_m = sensor->scale_m;
+ pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+ pll->bits_per_pixel = sensor->csi_format->compressed;
+
+ rval = smiapp_pll_calculate(&client->dev, &lim, pll);
+ if (rval < 0)
+ return rval;
+
+ sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz;
+ sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi;
+
+ return 0;
+}
+
+
+/*
+ *
+ * V4L2 Controls handling
+ *
+ */
+
+static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor)
+{
+ struct v4l2_ctrl *ctrl = sensor->exposure;
+ int max;
+
+ max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
+ + sensor->vblank->val
+ - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN];
+
+ ctrl->maximum = max;
+ if (ctrl->default_value > max)
+ ctrl->default_value = max;
+ if (ctrl->val > max)
+ ctrl->val = max;
+ if (ctrl->cur.val > max)
+ ctrl->cur.val = max;
+}
+
+/*
+ * Order matters.
+ *
+ * 1. Bits-per-pixel, descending.
+ * 2. Bits-per-pixel compressed, descending.
+ * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel
+ * orders must be defined.
+ */
+static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = {
+ { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, },
+ { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, },
+ { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+ { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, },
+ { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, },
+ { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, },
+ { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, },
+};
+
+const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" };
+
+#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \
+ - (unsigned long)smiapp_csi_data_formats) \
+ / sizeof(*smiapp_csi_data_formats))
+
+static u32 smiapp_pixel_order(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int flip = 0;
+
+ if (sensor->hflip) {
+ if (sensor->hflip->val)
+ flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP;
+
+ if (sensor->vflip->val)
+ flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP;
+ }
+
+ flip ^= sensor->hvflip_inv_mask;
+
+ dev_dbg(&client->dev, "flip %d\n", flip);
+ return sensor->default_pixel_order ^ flip;
+}
+
+static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int csi_format_idx =
+ to_csi_format_idx(sensor->csi_format) & ~3;
+ unsigned int internal_csi_format_idx =
+ to_csi_format_idx(sensor->internal_csi_format) & ~3;
+ unsigned int pixel_order = smiapp_pixel_order(sensor);
+
+ sensor->mbus_frame_fmts =
+ sensor->default_mbus_frame_fmts << pixel_order;
+ sensor->csi_format =
+ &smiapp_csi_data_formats[csi_format_idx + pixel_order];
+ sensor->internal_csi_format =
+ &smiapp_csi_data_formats[internal_csi_format_idx
+ + pixel_order];
+
+ BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order
+ >= ARRAY_SIZE(smiapp_csi_data_formats));
+ BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0);
+
+ dev_dbg(&client->dev, "new pixel order %s\n",
+ pixel_order_str[pixel_order]);
+}
+
+static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct smiapp_sensor *sensor =
+ container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler)
+ ->sensor;
+ u32 orient = 0;
+ int exposure;
+ int rval;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ return smiapp_write(
+ sensor,
+ SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val);
+
+ case V4L2_CID_EXPOSURE:
+ return smiapp_write(
+ sensor,
+ SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val);
+
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ if (sensor->streaming)
+ return -EBUSY;
+
+ if (sensor->hflip->val)
+ orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP;
+
+ if (sensor->vflip->val)
+ orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP;
+
+ orient ^= sensor->hvflip_inv_mask;
+ rval = smiapp_write(sensor,
+ SMIAPP_REG_U8_IMAGE_ORIENTATION,
+ orient);
+ if (rval < 0)
+ return rval;
+
+ smiapp_update_mbus_formats(sensor);
+
+ return 0;
+
+ case V4L2_CID_VBLANK:
+ exposure = sensor->exposure->val;
+
+ __smiapp_update_exposure_limits(sensor);
+
+ if (exposure > sensor->exposure->maximum) {
+ sensor->exposure->val =
+ sensor->exposure->maximum;
+ rval = smiapp_set_ctrl(
+ sensor->exposure);
+ if (rval < 0)
+ return rval;
+ }
+
+ return smiapp_write(
+ sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
+ + ctrl->val);
+
+ case V4L2_CID_HBLANK:
+ return smiapp_write(
+ sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
+ + ctrl->val);
+
+ case V4L2_CID_LINK_FREQ:
+ if (sensor->streaming)
+ return -EBUSY;
+
+ return smiapp_pll_update(sensor);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ctrl_ops smiapp_ctrl_ops = {
+ .s_ctrl = smiapp_set_ctrl,
+};
+
+static int smiapp_init_controls(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int max;
+ int rval;
+
+ rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7);
+ if (rval)
+ return rval;
+ sensor->pixel_array->ctrl_handler.lock = &sensor->mutex;
+
+ sensor->analog_gain = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN,
+ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN],
+ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX],
+ max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U),
+ sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]);
+
+ /* Exposure limits will be updated soon, use just something here. */
+ sensor->exposure = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_EXPOSURE, 0, 0, 1, 0);
+
+ sensor->hflip = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sensor->vflip = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ sensor->vblank = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_VBLANK, 0, 1, 1, 0);
+
+ if (sensor->vblank)
+ sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ sensor->hblank = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_HBLANK, 0, 1, 1, 0);
+
+ if (sensor->hblank)
+ sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE;
+
+ sensor->pixel_rate_parray = v4l2_ctrl_new_std(
+ &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0);
+
+ if (sensor->pixel_array->ctrl_handler.error) {
+ dev_err(&client->dev,
+ "pixel array controls initialization failed (%d)\n",
+ sensor->pixel_array->ctrl_handler.error);
+ rval = sensor->pixel_array->ctrl_handler.error;
+ goto error;
+ }
+
+ sensor->pixel_array->sd.ctrl_handler =
+ &sensor->pixel_array->ctrl_handler;
+
+ v4l2_ctrl_cluster(2, &sensor->hflip);
+
+ rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0);
+ if (rval)
+ goto error;
+ sensor->src->ctrl_handler.lock = &sensor->mutex;
+
+ for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++);
+
+ sensor->link_freq = v4l2_ctrl_new_int_menu(
+ &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_LINK_FREQ, max, 0,
+ sensor->platform_data->op_sys_clock);
+
+ sensor->pixel_rate_csi = v4l2_ctrl_new_std(
+ &sensor->src->ctrl_handler, &smiapp_ctrl_ops,
+ V4L2_CID_PIXEL_RATE, 0, 0, 1, 0);
+
+ if (sensor->src->ctrl_handler.error) {
+ dev_err(&client->dev,
+ "src controls initialization failed (%d)\n",
+ sensor->src->ctrl_handler.error);
+ rval = sensor->src->ctrl_handler.error;
+ goto error;
+ }
+
+ sensor->src->sd.ctrl_handler =
+ &sensor->src->ctrl_handler;
+
+ return 0;
+
+error:
+ v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler);
+ v4l2_ctrl_handler_free(&sensor->src->ctrl_handler);
+
+ return rval;
+}
+
+static void smiapp_free_controls(struct smiapp_sensor *sensor)
+{
+ unsigned int i;
+
+ for (i = 0; i < sensor->ssds_used; i++)
+ v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler);
+}
+
+static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit,
+ unsigned int n)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int i;
+ u32 val;
+ int rval;
+
+ for (i = 0; i < n; i++) {
+ rval = smiapp_read(
+ sensor, smiapp_reg_limits[limit[i]].addr, &val);
+ if (rval)
+ return rval;
+ sensor->limits[limit[i]] = val;
+ dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n",
+ smiapp_reg_limits[limit[i]].addr,
+ smiapp_reg_limits[limit[i]].what, val, val);
+ }
+
+ return 0;
+}
+
+static int smiapp_get_all_limits(struct smiapp_sensor *sensor)
+{
+ unsigned int i;
+ int rval;
+
+ for (i = 0; i < SMIAPP_LIMIT_LAST; i++) {
+ rval = smiapp_get_limits(sensor, &i, 1);
+ if (rval < 0)
+ return rval;
+ }
+
+ if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0)
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16);
+
+ return 0;
+}
+
+static int smiapp_get_limits_binning(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ static u32 const limits[] = {
+ SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN,
+ SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN,
+ SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN,
+ SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN,
+ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN,
+ };
+ static u32 const limits_replace[] = {
+ SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES,
+ SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES,
+ SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK,
+ SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK,
+ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN,
+ SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN,
+ };
+ unsigned int i;
+ int rval;
+
+ if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] ==
+ SMIAPP_BINNING_CAPABILITY_NO) {
+ for (i = 0; i < ARRAY_SIZE(limits); i++)
+ sensor->limits[limits[i]] =
+ sensor->limits[limits_replace[i]];
+
+ return 0;
+ }
+
+ rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits));
+ if (rval < 0)
+ return rval;
+
+ /*
+ * Sanity check whether the binning limits are valid. If not,
+ * use the non-binning ones.
+ */
+ if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN]
+ && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN]
+ && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN])
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(limits); i++) {
+ dev_dbg(&client->dev,
+ "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n",
+ smiapp_reg_limits[limits[i]].addr,
+ smiapp_reg_limits[limits[i]].what,
+ sensor->limits[limits_replace[i]],
+ sensor->limits[limits_replace[i]]);
+ sensor->limits[limits[i]] =
+ sensor->limits[limits_replace[i]];
+ }
+
+ return 0;
+}
+
+static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int type, n;
+ unsigned int i, pixel_order;
+ int rval;
+
+ rval = smiapp_read(
+ sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type);
+ if (rval)
+ return rval;
+
+ dev_dbg(&client->dev, "data_format_model_type %d\n", type);
+
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER,
+ &pixel_order);
+ if (rval)
+ return rval;
+
+ if (pixel_order >= ARRAY_SIZE(pixel_order_str)) {
+ dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order);
+ return -EINVAL;
+ }
+
+ dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order,
+ pixel_order_str[pixel_order]);
+
+ switch (type) {
+ case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL:
+ n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N;
+ break;
+ case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED:
+ n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor->default_pixel_order = pixel_order;
+ sensor->mbus_frame_fmts = 0;
+
+ for (i = 0; i < n; i++) {
+ unsigned int fmt, j;
+
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt);
+ if (rval)
+ return rval;
+
+ dev_dbg(&client->dev, "bpp %d, compressed %d\n",
+ fmt >> 8, (u8)fmt);
+
+ for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) {
+ const struct smiapp_csi_data_format *f =
+ &smiapp_csi_data_formats[j];
+
+ if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG)
+ continue;
+
+ if (f->width != fmt >> 8 || f->compressed != (u8)fmt)
+ continue;
+
+ dev_dbg(&client->dev, "jolly good! %d\n", j);
+
+ sensor->default_mbus_frame_fmts |= 1 << j;
+ if (!sensor->csi_format) {
+ sensor->csi_format = f;
+ sensor->internal_csi_format = f;
+ }
+ }
+ }
+
+ if (!sensor->csi_format) {
+ dev_err(&client->dev, "no supported mbus code found\n");
+ return -EINVAL;
+ }
+
+ smiapp_update_mbus_formats(sensor);
+
+ return 0;
+}
+
+static void smiapp_update_blanking(struct smiapp_sensor *sensor)
+{
+ struct v4l2_ctrl *vblank = sensor->vblank;
+ struct v4l2_ctrl *hblank = sensor->hblank;
+
+ vblank->minimum =
+ max_t(int,
+ sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES],
+ sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height);
+ vblank->maximum =
+ sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height;
+
+ vblank->val = clamp_t(int, vblank->val,
+ vblank->minimum, vblank->maximum);
+ vblank->default_value = vblank->minimum;
+ vblank->val = vblank->val;
+ vblank->cur.val = vblank->val;
+
+ hblank->minimum =
+ max_t(int,
+ sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width,
+ sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]);
+ hblank->maximum =
+ sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] -
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width;
+
+ hblank->val = clamp_t(int, hblank->val,
+ hblank->minimum, hblank->maximum);
+ hblank->default_value = hblank->minimum;
+ hblank->val = hblank->val;
+ hblank->cur.val = hblank->val;
+
+ __smiapp_update_exposure_limits(sensor);
+}
+
+static int smiapp_update_mode(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int binning_mode;
+ int rval;
+
+ dev_dbg(&client->dev, "frame size: %dx%d\n",
+ sensor->src->crop[SMIAPP_PAD_SRC].width,
+ sensor->src->crop[SMIAPP_PAD_SRC].height);
+ dev_dbg(&client->dev, "csi format width: %d\n",
+ sensor->csi_format->width);
+
+ /* Binning has to be set up here; it affects limits */
+ if (sensor->binning_horizontal == 1 &&
+ sensor->binning_vertical == 1) {
+ binning_mode = 0;
+ } else {
+ u8 binning_type =
+ (sensor->binning_horizontal << 4)
+ | sensor->binning_vertical;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type);
+ if (rval < 0)
+ return rval;
+
+ binning_mode = 1;
+ }
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode);
+ if (rval < 0)
+ return rval;
+
+ /* Get updated limits due to binning */
+ rval = smiapp_get_limits_binning(sensor);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_pll_update(sensor);
+ if (rval < 0)
+ return rval;
+
+ /* Output from pixel array, including blanking */
+ smiapp_update_blanking(sensor);
+
+ dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val);
+ dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val);
+
+ dev_dbg(&client->dev, "real timeperframe\t100/%d\n",
+ sensor->pll.vt_pix_clk_freq_hz /
+ ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width
+ + sensor->hblank->val) *
+ (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height
+ + sensor->vblank->val) / 100));
+
+ return 0;
+}
+
+/*
+ *
+ * SMIA++ NVM handling
+ *
+ */
+static int smiapp_read_nvm(struct smiapp_sensor *sensor,
+ unsigned char *nvm)
+{
+ u32 i, s, p, np, v;
+ int rval = 0, rval2;
+
+ np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE;
+ for (p = 0; p < np; p++) {
+ rval = smiapp_write(
+ sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p);
+ if (rval)
+ goto out;
+
+ rval = smiapp_write(sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL,
+ SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN |
+ SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN);
+ if (rval)
+ goto out;
+
+ for (i = 0; i < 1000; i++) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s);
+
+ if (rval)
+ goto out;
+
+ if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY)
+ break;
+
+ if (--i == 0) {
+ rval = -ETIMEDOUT;
+ goto out;
+ }
+
+ }
+
+ for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) {
+ rval = smiapp_read(
+ sensor,
+ SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i,
+ &v);
+ if (rval)
+ goto out;
+
+ *nvm++ = v;
+ }
+ }
+
+out:
+ rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0);
+ if (rval < 0)
+ return rval;
+ else
+ return rval2;
+}
+
+/*
+ *
+ * SMIA++ CCI address control
+ *
+ */
+static int smiapp_change_cci_addr(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+ u32 val;
+
+ client->addr = sensor->platform_data->i2c_addr_dfl;
+
+ rval = smiapp_write(sensor,
+ SMIAPP_REG_U8_CCI_ADDRESS_CONTROL,
+ sensor->platform_data->i2c_addr_alt << 1);
+ if (rval)
+ return rval;
+
+ client->addr = sensor->platform_data->i2c_addr_alt;
+
+ /* verify addr change went ok */
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val);
+ if (rval)
+ return rval;
+
+ if (val != sensor->platform_data->i2c_addr_alt << 1)
+ return -ENODEV;
+
+ return 0;
+}
+
+/*
+ *
+ * SMIA++ Mode Control
+ *
+ */
+static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor)
+{
+ struct smiapp_flash_strobe_parms *strobe_setup;
+ unsigned int ext_freq = sensor->platform_data->ext_clk;
+ u32 tmp;
+ u32 strobe_adjustment;
+ u32 strobe_width_high_rs;
+ int rval;
+
+ strobe_setup = sensor->platform_data->strobe_setup;
+
+ /*
+ * How to calculate registers related to strobe length. Please
+ * do not change, or if you do at least know what you're
+ * doing. :-)
+ *
+ * Sakari Ailus <sakari.ailus@maxwell.research.nokia.com> 2010-10-25
+ *
+ * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl
+ * / EXTCLK freq [Hz]) * flash_strobe_adjustment
+ *
+ * tFlash_strobe_width_ctrl E N, [1 - 0xffff]
+ * flash_strobe_adjustment E N, [1 - 0xff]
+ *
+ * The formula above is written as below to keep it on one
+ * line:
+ *
+ * l / 10^6 = w / e * a
+ *
+ * Let's mark w * a by x:
+ *
+ * x = w * a
+ *
+ * Thus, we get:
+ *
+ * x = l * e / 10^6
+ *
+ * The strobe width must be at least as long as requested,
+ * thus rounding upwards is needed.
+ *
+ * x = (l * e + 10^6 - 1) / 10^6
+ * -----------------------------
+ *
+ * Maximum possible accuracy is wanted at all times. Thus keep
+ * a as small as possible.
+ *
+ * Calculate a, assuming maximum w, with rounding upwards:
+ *
+ * a = (x + (2^16 - 1) - 1) / (2^16 - 1)
+ * -------------------------------------
+ *
+ * Thus, we also get w, with that a, with rounding upwards:
+ *
+ * w = (x + a - 1) / a
+ * -------------------
+ *
+ * To get limits:
+ *
+ * x E [1, (2^16 - 1) * (2^8 - 1)]
+ *
+ * Substituting maximum x to the original formula (with rounding),
+ * the maximum l is thus
+ *
+ * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1
+ *
+ * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e
+ * --------------------------------------------------
+ *
+ * flash_strobe_length must be clamped between 1 and
+ * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq.
+ *
+ * Then,
+ *
+ * flash_strobe_adjustment = ((flash_strobe_length *
+ * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1)
+ *
+ * tFlash_strobe_width_ctrl = ((flash_strobe_length *
+ * EXTCLK freq + 10^6 - 1) / 10^6 +
+ * flash_strobe_adjustment - 1) / flash_strobe_adjustment
+ */
+ tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) -
+ 1000000 + 1, ext_freq);
+ strobe_setup->strobe_width_high_us =
+ clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp);
+
+ tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq +
+ 1000000 - 1), 1000000ULL);
+ strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1);
+ strobe_width_high_rs = (tmp + strobe_adjustment - 1) /
+ strobe_adjustment;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS,
+ strobe_setup->mode);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT,
+ strobe_adjustment);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL,
+ strobe_width_high_rs);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL,
+ strobe_setup->strobe_delay);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT,
+ strobe_setup->stobe_start_point);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS,
+ strobe_setup->trigger);
+
+out:
+ sensor->platform_data->strobe_setup->trigger = 0;
+
+ return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int smiapp_power_on(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int sleep;
+ int rval;
+
+ rval = regulator_enable(sensor->vana);
+ if (rval) {
+ dev_err(&client->dev, "failed to enable vana regulator\n");
+ return rval;
+ }
+ usleep_range(1000, 1000);
+
+ if (sensor->platform_data->set_xclk)
+ rval = sensor->platform_data->set_xclk(
+ &sensor->src->sd, sensor->platform_data->ext_clk);
+ else
+ rval = clk_enable(sensor->ext_clk);
+ if (rval < 0) {
+ dev_dbg(&client->dev, "failed to set xclk\n");
+ goto out_xclk_fail;
+ }
+ usleep_range(1000, 1000);
+
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 1);
+
+ sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk);
+ usleep_range(sleep, sleep);
+
+ /*
+ * Failures to respond to the address change command have been noticed.
+ * Those failures seem to be caused by the sensor requiring a longer
+ * boot time than advertised. An additional 10ms delay seems to work
+ * around the issue, but the SMIA++ I2C write retry hack makes the delay
+ * unnecessary. The failures need to be investigated to find a proper
+ * fix, and a delay will likely need to be added here if the I2C write
+ * retry hack is reverted before the root cause of the boot time issue
+ * is found.
+ */
+
+ if (sensor->platform_data->i2c_addr_alt) {
+ rval = smiapp_change_cci_addr(sensor);
+ if (rval) {
+ dev_err(&client->dev, "cci address change error\n");
+ goto out_cci_addr_fail;
+ }
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET,
+ SMIAPP_SOFTWARE_RESET);
+ if (rval < 0) {
+ dev_err(&client->dev, "software reset failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ if (sensor->platform_data->i2c_addr_alt) {
+ rval = smiapp_change_cci_addr(sensor);
+ if (rval) {
+ dev_err(&client->dev, "cci address change error\n");
+ goto out_cci_addr_fail;
+ }
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE,
+ SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR);
+ if (rval) {
+ dev_err(&client->dev, "compression mode set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ,
+ sensor->platform_data->ext_clk / (1000000 / (1 << 8)));
+ if (rval) {
+ dev_err(&client->dev, "extclk frequency set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE,
+ sensor->platform_data->lanes - 1);
+ if (rval) {
+ dev_err(&client->dev, "csi lane mode set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL,
+ SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE);
+ if (rval) {
+ dev_err(&client->dev, "fast standby set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE,
+ sensor->platform_data->csi_signalling_mode);
+ if (rval) {
+ dev_err(&client->dev, "csi signalling mode set failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ /* DPHY control done by sensor based on requested link rate */
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL,
+ SMIAPP_DPHY_CTRL_UI);
+ if (rval < 0)
+ return rval;
+
+ rval = smiapp_call_quirk(sensor, post_poweron);
+ if (rval) {
+ dev_err(&client->dev, "post_poweron quirks failed\n");
+ goto out_cci_addr_fail;
+ }
+
+ /* Are we still initialising...? If yes, return here. */
+ if (!sensor->pixel_array)
+ return 0;
+
+ rval = v4l2_ctrl_handler_setup(
+ &sensor->pixel_array->ctrl_handler);
+ if (rval)
+ goto out_cci_addr_fail;
+
+ rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler);
+ if (rval)
+ goto out_cci_addr_fail;
+
+ mutex_lock(&sensor->mutex);
+ rval = smiapp_update_mode(sensor);
+ mutex_unlock(&sensor->mutex);
+ if (rval < 0)
+ goto out_cci_addr_fail;
+
+ return 0;
+
+out_cci_addr_fail:
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 0);
+ if (sensor->platform_data->set_xclk)
+ sensor->platform_data->set_xclk(&sensor->src->sd, 0);
+ else
+ clk_disable(sensor->ext_clk);
+
+out_xclk_fail:
+ regulator_disable(sensor->vana);
+ return rval;
+}
+
+static void smiapp_power_off(struct smiapp_sensor *sensor)
+{
+ /*
+ * Currently power/clock to lens are enable/disabled separately
+ * but they are essentially the same signals. So if the sensor is
+ * powered off while the lens is powered on the sensor does not
+ * really see a power off and next time the cci address change
+ * will fail. So do a soft reset explicitly here.
+ */
+ if (sensor->platform_data->i2c_addr_alt)
+ smiapp_write(sensor,
+ SMIAPP_REG_U8_SOFTWARE_RESET,
+ SMIAPP_SOFTWARE_RESET);
+
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 0);
+ if (sensor->platform_data->set_xclk)
+ sensor->platform_data->set_xclk(&sensor->src->sd, 0);
+ else
+ clk_disable(sensor->ext_clk);
+ usleep_range(5000, 5000);
+ regulator_disable(sensor->vana);
+ sensor->streaming = 0;
+}
+
+static int smiapp_set_power(struct v4l2_subdev *subdev, int on)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int ret = 0;
+
+ mutex_lock(&sensor->power_mutex);
+
+ /*
+ * If the power count is modified from 0 to != 0 or from != 0
+ * to 0, update the power state.
+ */
+ if (!sensor->power_count == !on)
+ goto out;
+
+ if (on) {
+ /* Power on and perform initialisation. */
+ ret = smiapp_power_on(sensor);
+ if (ret < 0)
+ goto out;
+ } else {
+ smiapp_power_off(sensor);
+ }
+
+ /* Update the power count. */
+ sensor->power_count += on ? 1 : -1;
+ WARN_ON(sensor->power_count < 0);
+
+out:
+ mutex_unlock(&sensor->power_mutex);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Video stream management
+ */
+
+static int smiapp_start_streaming(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT,
+ (sensor->csi_format->width << 8) |
+ sensor->csi_format->compressed);
+ if (rval)
+ goto out;
+
+ rval = smiapp_pll_configure(sensor);
+ if (rval)
+ goto out;
+
+ /* Analog crop start coordinates */
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top);
+ if (rval < 0)
+ goto out;
+
+ /* Analog crop end coordinates */
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_X_ADDR_END,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left
+ + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_Y_ADDR_END,
+ sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top
+ + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1);
+ if (rval < 0)
+ goto out;
+
+ /*
+ * Output from pixel array, including blanking, is set using
+ * controls below. No need to set here.
+ */
+
+ /* Digital crop */
+ if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
+ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].left);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].top);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].width);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(
+ sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT,
+ sensor->scaler->crop[SMIAPP_PAD_SINK].height);
+ if (rval < 0)
+ goto out;
+ }
+
+ /* Scaling */
+ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ != SMIAPP_SCALING_CAPABILITY_NONE) {
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE,
+ sensor->scaling_mode);
+ if (rval < 0)
+ goto out;
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M,
+ sensor->scale_m);
+ if (rval < 0)
+ goto out;
+ }
+
+ /* Output size from sensor */
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE,
+ sensor->src->crop[SMIAPP_PAD_SRC].width);
+ if (rval < 0)
+ goto out;
+ rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE,
+ sensor->src->crop[SMIAPP_PAD_SRC].height);
+ if (rval < 0)
+ goto out;
+
+ if ((sensor->flash_capability &
+ (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE |
+ SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) &&
+ sensor->platform_data->strobe_setup != NULL &&
+ sensor->platform_data->strobe_setup->trigger != 0) {
+ rval = smiapp_setup_flash_strobe(sensor);
+ if (rval)
+ goto out;
+ }
+
+ rval = smiapp_call_quirk(sensor, pre_streamon);
+ if (rval) {
+ dev_err(&client->dev, "pre_streamon quirks failed\n");
+ goto out;
+ }
+
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT,
+ SMIAPP_MODE_SELECT_STREAMING);
+
+out:
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+
+static int smiapp_stop_streaming(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+ rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT,
+ SMIAPP_MODE_SELECT_SOFTWARE_STANDBY);
+ if (rval)
+ goto out;
+
+ rval = smiapp_call_quirk(sensor, post_streamoff);
+ if (rval)
+ dev_err(&client->dev, "post_streamoff quirks failed\n");
+
+out:
+ mutex_unlock(&sensor->mutex);
+ return rval;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev video operations
+ */
+
+static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ if (sensor->streaming == enable)
+ return 0;
+
+ if (enable) {
+ sensor->streaming = 1;
+ rval = smiapp_start_streaming(sensor);
+ if (rval < 0)
+ sensor->streaming = 0;
+ } else {
+ rval = smiapp_stop_streaming(sensor);
+ sensor->streaming = 0;
+ }
+
+ return rval;
+}
+
+static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int i;
+ int idx = -1;
+ int rval = -EINVAL;
+
+ mutex_lock(&sensor->mutex);
+
+ dev_err(&client->dev, "subdev %s, pad %d, index %d\n",
+ subdev->name, code->pad, code->index);
+
+ if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) {
+ if (code->index)
+ goto out;
+
+ code->code = sensor->internal_csi_format->code;
+ rval = 0;
+ goto out;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
+ if (sensor->mbus_frame_fmts & (1 << i))
+ idx++;
+
+ if (idx == code->index) {
+ code->code = smiapp_csi_data_formats[i].code;
+ dev_err(&client->dev, "found index %d, i %d, code %x\n",
+ code->index, i, code->code);
+ rval = 0;
+ break;
+ }
+ }
+
+out:
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+
+static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev,
+ unsigned int pad)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+
+ if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC)
+ return sensor->csi_format->code;
+ else
+ return sensor->internal_csi_format->code;
+}
+
+static int __smiapp_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad);
+ } else {
+ struct v4l2_rect *r;
+
+ if (fmt->pad == ssd->source_pad)
+ r = &ssd->crop[ssd->source_pad];
+ else
+ r = &ssd->sink_fmt;
+
+ fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad);
+ fmt->format.width = r->width;
+ fmt->format.height = r->height;
+ }
+
+ return 0;
+}
+
+static int smiapp_get_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+ rval = __smiapp_get_format(subdev, fh, fmt);
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+
+static void smiapp_get_crop_compose(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_rect **crops,
+ struct v4l2_rect **comps, int which)
+{
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ unsigned int i;
+
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ if (crops)
+ for (i = 0; i < subdev->entity.num_pads; i++)
+ crops[i] = &ssd->crop[i];
+ if (comps)
+ *comps = &ssd->compose;
+ } else {
+ if (crops) {
+ for (i = 0; i < subdev->entity.num_pads; i++) {
+ crops[i] = v4l2_subdev_get_try_crop(fh, i);
+ BUG_ON(!crops[i]);
+ }
+ }
+ if (comps) {
+ *comps = v4l2_subdev_get_try_compose(fh,
+ SMIAPP_PAD_SINK);
+ BUG_ON(!*comps);
+ }
+ }
+}
+
+/* Changes require propagation only on sink pad. */
+static void smiapp_propagate(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh, int which,
+ int target)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *comp, *crops[SMIAPP_PADS];
+
+ smiapp_get_crop_compose(subdev, fh, crops, &comp, which);
+
+ switch (target) {
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ comp->width = crops[SMIAPP_PAD_SINK]->width;
+ comp->height = crops[SMIAPP_PAD_SINK]->height;
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ if (ssd == sensor->scaler) {
+ sensor->scale_m =
+ sensor->limits[
+ SMIAPP_LIMIT_SCALER_N_MIN];
+ sensor->scaling_mode =
+ SMIAPP_SCALING_MODE_NONE;
+ } else if (ssd == sensor->binner) {
+ sensor->binning_horizontal = 1;
+ sensor->binning_vertical = 1;
+ }
+ }
+ /* Fall through */
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ *crops[SMIAPP_PAD_SRC] = *comp;
+ break;
+ default:
+ BUG();
+ }
+}
+
+static const struct smiapp_csi_data_format
+*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code)
+{
+ const struct smiapp_csi_data_format *csi_format = sensor->csi_format;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) {
+ if (sensor->mbus_frame_fmts & (1 << i)
+ && smiapp_csi_data_formats[i].code == code)
+ return &smiapp_csi_data_formats[i];
+ }
+
+ return csi_format;
+}
+
+static int smiapp_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *crops[SMIAPP_PADS];
+
+ mutex_lock(&sensor->mutex);
+
+ /*
+ * Media bus code is changeable on src subdev's source pad. On
+ * other source pads we just get format here.
+ */
+ if (fmt->pad == ssd->source_pad) {
+ u32 code = fmt->format.code;
+ int rval = __smiapp_get_format(subdev, fh, fmt);
+
+ if (!rval && subdev == &sensor->src->sd) {
+ const struct smiapp_csi_data_format *csi_format =
+ smiapp_validate_csi_data_format(sensor, code);
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ sensor->csi_format = csi_format;
+ fmt->format.code = csi_format->code;
+ }
+
+ mutex_unlock(&sensor->mutex);
+ return rval;
+ }
+
+ /* Sink pad. Width and height are changeable here. */
+ fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad);
+ fmt->format.width &= ~1;
+ fmt->format.height &= ~1;
+
+ fmt->format.width =
+ clamp(fmt->format.width,
+ sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE],
+ sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]);
+ fmt->format.height =
+ clamp(fmt->format.height,
+ sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE],
+ sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]);
+
+ smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which);
+
+ crops[ssd->sink_pad]->left = 0;
+ crops[ssd->sink_pad]->top = 0;
+ crops[ssd->sink_pad]->width = fmt->format.width;
+ crops[ssd->sink_pad]->height = fmt->format.height;
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ ssd->sink_fmt = *crops[ssd->sink_pad];
+ smiapp_propagate(subdev, fh, fmt->which,
+ V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL);
+
+ mutex_unlock(&sensor->mutex);
+
+ return 0;
+}
+
+/*
+ * Calculate goodness of scaled image size compared to expected image
+ * size and flags provided.
+ */
+#define SCALING_GOODNESS 100000
+#define SCALING_GOODNESS_EXTREME 100000000
+static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w,
+ int h, int ask_h, u32 flags)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ int val = 0;
+
+ w &= ~1;
+ ask_w &= ~1;
+ h &= ~1;
+ ask_h &= ~1;
+
+ if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) {
+ if (w < ask_w)
+ val -= SCALING_GOODNESS;
+ if (h < ask_h)
+ val -= SCALING_GOODNESS;
+ }
+
+ if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) {
+ if (w > ask_w)
+ val -= SCALING_GOODNESS;
+ if (h > ask_h)
+ val -= SCALING_GOODNESS;
+ }
+
+ val -= abs(w - ask_w);
+ val -= abs(h - ask_h);
+
+ if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE])
+ val -= SCALING_GOODNESS_EXTREME;
+
+ dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n",
+ w, ask_h, h, ask_h, val);
+
+ return val;
+}
+
+static void smiapp_set_compose_binner(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel,
+ struct v4l2_rect **crops,
+ struct v4l2_rect *comp)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int i;
+ unsigned int binh = 1, binv = 1;
+ unsigned int best = scaling_goodness(
+ subdev,
+ crops[SMIAPP_PAD_SINK]->width, sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags);
+
+ for (i = 0; i < sensor->nbinning_subtypes; i++) {
+ int this = scaling_goodness(
+ subdev,
+ crops[SMIAPP_PAD_SINK]->width
+ / sensor->binning_subtypes[i].horizontal,
+ sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height
+ / sensor->binning_subtypes[i].vertical,
+ sel->r.height, sel->flags);
+
+ if (this > best) {
+ binh = sensor->binning_subtypes[i].horizontal;
+ binv = sensor->binning_subtypes[i].vertical;
+ best = this;
+ }
+ }
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ sensor->binning_vertical = binv;
+ sensor->binning_horizontal = binh;
+ }
+
+ sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1;
+ sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1;
+}
+
+/*
+ * Calculate best scaling ratio and mode for given output resolution.
+ *
+ * Try all of these: horizontal ratio, vertical ratio and smallest
+ * size possible (horizontally).
+ *
+ * Also try whether horizontal scaler or full scaler gives a better
+ * result.
+ */
+static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel,
+ struct v4l2_rect **crops,
+ struct v4l2_rect *comp)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ u32 min, max, a, b, max_m;
+ u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+ int mode = SMIAPP_SCALING_MODE_HORIZONTAL;
+ u32 try[4];
+ u32 ntry = 0;
+ unsigned int i;
+ int best = INT_MIN;
+
+ sel->r.width = min_t(unsigned int, sel->r.width,
+ crops[SMIAPP_PAD_SINK]->width);
+ sel->r.height = min_t(unsigned int, sel->r.height,
+ crops[SMIAPP_PAD_SINK]->height);
+
+ a = crops[SMIAPP_PAD_SINK]->width
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width;
+ b = crops[SMIAPP_PAD_SINK]->height
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height;
+ max_m = crops[SMIAPP_PAD_SINK]->width
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]
+ / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE];
+
+ a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
+ max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
+ b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
+ max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
+ max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX],
+ max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN]));
+
+ dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m);
+
+ min = min(max_m, min(a, b));
+ max = min(max_m, max(a, b));
+
+ try[ntry] = min;
+ ntry++;
+ if (min != max) {
+ try[ntry] = max;
+ ntry++;
+ }
+ if (max != max_m) {
+ try[ntry] = min + 1;
+ ntry++;
+ if (min != max) {
+ try[ntry] = max + 1;
+ ntry++;
+ }
+ }
+
+ for (i = 0; i < ntry; i++) {
+ int this = scaling_goodness(
+ subdev,
+ crops[SMIAPP_PAD_SINK]->width
+ / try[i]
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
+ sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height,
+ sel->r.height,
+ sel->flags);
+
+ dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i);
+
+ if (this > best) {
+ scale_m = try[i];
+ mode = SMIAPP_SCALING_MODE_HORIZONTAL;
+ best = this;
+ }
+
+ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
+ continue;
+
+ this = scaling_goodness(
+ subdev, crops[SMIAPP_PAD_SINK]->width
+ / try[i]
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
+ sel->r.width,
+ crops[SMIAPP_PAD_SINK]->height
+ / try[i]
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN],
+ sel->r.height,
+ sel->flags);
+
+ if (this > best) {
+ scale_m = try[i];
+ mode = SMIAPP_SCALING_MODE_BOTH;
+ best = this;
+ }
+ }
+
+ sel->r.width =
+ (crops[SMIAPP_PAD_SINK]->width
+ / scale_m
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1;
+ if (mode == SMIAPP_SCALING_MODE_BOTH)
+ sel->r.height =
+ (crops[SMIAPP_PAD_SINK]->height
+ / scale_m
+ * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN])
+ & ~1;
+ else
+ sel->r.height = crops[SMIAPP_PAD_SINK]->height;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ sensor->scale_m = scale_m;
+ sensor->scaling_mode = mode;
+ }
+}
+/* We're only called on source pads. This function sets scaling. */
+static int smiapp_set_compose(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *comp, *crops[SMIAPP_PADS];
+
+ smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
+
+ sel->r.top = 0;
+ sel->r.left = 0;
+
+ if (ssd == sensor->binner)
+ smiapp_set_compose_binner(subdev, fh, sel, crops, comp);
+ else
+ smiapp_set_compose_scaler(subdev, fh, sel, crops, comp);
+
+ *comp = sel->r;
+ smiapp_propagate(subdev, fh, sel->which,
+ V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ return smiapp_update_mode(sensor);
+
+ return 0;
+}
+
+static int __smiapp_sel_supported(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+
+ /* We only implement crop in three places. */
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ if (ssd == sensor->pixel_array
+ && sel->pad == SMIAPP_PA_PAD_SRC)
+ return 0;
+ if (ssd == sensor->src
+ && sel->pad == SMIAPP_PAD_SRC)
+ return 0;
+ if (ssd == sensor->scaler
+ && sel->pad == SMIAPP_PAD_SINK
+ && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
+ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP)
+ return 0;
+ return -EINVAL;
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ if (sel->pad == ssd->source_pad)
+ return -EINVAL;
+ if (ssd == sensor->binner)
+ return 0;
+ if (ssd == sensor->scaler
+ && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ != SMIAPP_SCALING_CAPABILITY_NONE)
+ return 0;
+ /* Fall through */
+ default:
+ return -EINVAL;
+ }
+}
+
+static int smiapp_set_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *src_size, *crops[SMIAPP_PADS];
+ struct v4l2_rect _r;
+
+ smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ if (sel->pad == ssd->sink_pad)
+ src_size = &ssd->sink_fmt;
+ else
+ src_size = &ssd->compose;
+ } else {
+ if (sel->pad == ssd->sink_pad) {
+ _r.left = 0;
+ _r.top = 0;
+ _r.width = v4l2_subdev_get_try_format(fh, sel->pad)
+ ->width;
+ _r.height = v4l2_subdev_get_try_format(fh, sel->pad)
+ ->height;
+ src_size = &_r;
+ } else {
+ src_size =
+ v4l2_subdev_get_try_compose(
+ fh, ssd->sink_pad);
+ }
+ }
+
+ if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) {
+ sel->r.left = 0;
+ sel->r.top = 0;
+ }
+
+ sel->r.width = min(sel->r.width, src_size->width);
+ sel->r.height = min(sel->r.height, src_size->height);
+
+ sel->r.left = min(sel->r.left, src_size->width - sel->r.width);
+ sel->r.top = min(sel->r.top, src_size->height - sel->r.height);
+
+ *crops[sel->pad] = sel->r;
+
+ if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK)
+ smiapp_propagate(subdev, fh, sel->which,
+ V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL);
+
+ return 0;
+}
+
+static int __smiapp_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct smiapp_subdev *ssd = to_smiapp_subdev(subdev);
+ struct v4l2_rect *comp, *crops[SMIAPP_PADS];
+ struct v4l2_rect sink_fmt;
+ int ret;
+
+ ret = __smiapp_sel_supported(subdev, sel);
+ if (ret)
+ return ret;
+
+ smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ sink_fmt = ssd->sink_fmt;
+ } else {
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_get_try_format(fh, ssd->sink_pad);
+
+ sink_fmt.left = 0;
+ sink_fmt.top = 0;
+ sink_fmt.width = fmt->width;
+ sink_fmt.height = fmt->height;
+ }
+
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS:
+ if (ssd == sensor->pixel_array) {
+ sel->r.width =
+ sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
+ sel->r.height =
+ sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
+ } else if (sel->pad == ssd->sink_pad) {
+ sel->r = sink_fmt;
+ } else {
+ sel->r = *comp;
+ }
+ break;
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS:
+ sel->r = *crops[sel->pad];
+ break;
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ sel->r = *comp;
+ break;
+ }
+
+ return 0;
+}
+
+static int smiapp_get_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ mutex_lock(&sensor->mutex);
+ rval = __smiapp_get_selection(subdev, fh, sel);
+ mutex_unlock(&sensor->mutex);
+
+ return rval;
+}
+static int smiapp_set_selection(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_selection *sel)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int ret;
+
+ ret = __smiapp_sel_supported(subdev, sel);
+ if (ret)
+ return ret;
+
+ mutex_lock(&sensor->mutex);
+
+ sel->r.left = max(0, sel->r.left & ~1);
+ sel->r.top = max(0, sel->r.top & ~1);
+ sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags));
+ sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags));
+
+ sel->r.width = max_t(unsigned int,
+ sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE],
+ sel->r.width);
+ sel->r.height = max_t(unsigned int,
+ sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE],
+ sel->r.height);
+
+ switch (sel->target) {
+ case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL:
+ ret = smiapp_set_crop(subdev, fh, sel);
+ break;
+ case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL:
+ ret = smiapp_set_compose(subdev, fh, sel);
+ break;
+ default:
+ BUG();
+ }
+
+ mutex_unlock(&sensor->mutex);
+ return ret;
+}
+
+static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+
+ *frames = sensor->frame_skip;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * sysfs attributes
+ */
+
+static ssize_t
+smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev));
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int nbytes;
+
+ if (!sensor->dev_init_done)
+ return -EBUSY;
+
+ if (!sensor->nvm_size) {
+ /* NVM not read yet - read it now */
+ sensor->nvm_size = sensor->platform_data->nvm_size;
+ if (smiapp_set_power(subdev, 1) < 0)
+ return -ENODEV;
+ if (smiapp_read_nvm(sensor, sensor->nvm)) {
+ dev_err(&client->dev, "nvm read failed\n");
+ return -ENODEV;
+ }
+ smiapp_set_power(subdev, 0);
+ }
+ /*
+ * NVM is still way below a PAGE_SIZE, so we can safely
+ * assume this for now.
+ */
+ nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE);
+ memcpy(buf, sensor->nvm, nbytes);
+
+ return nbytes;
+}
+static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL);
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+static int smiapp_identify_module(struct v4l2_subdev *subdev)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_module_info *minfo = &sensor->minfo;
+ unsigned int i;
+ int rval = 0;
+
+ minfo->name = SMIAPP_NAME;
+
+ /* Module info */
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID,
+ &minfo->manufacturer_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID,
+ &minfo->model_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_REVISION_NUMBER_MAJOR,
+ &minfo->revision_number_major);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_REVISION_NUMBER_MINOR,
+ &minfo->revision_number_minor);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_MODULE_DATE_YEAR,
+ &minfo->module_year);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_MODULE_DATE_MONTH,
+ &minfo->module_month);
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY,
+ &minfo->module_day);
+
+ /* Sensor info */
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID,
+ &minfo->sensor_manufacturer_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U16_SENSOR_MODEL_ID,
+ &minfo->sensor_model_id);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_SENSOR_REVISION_NUMBER,
+ &minfo->sensor_revision_number);
+ if (!rval)
+ rval = smiapp_read_8only(sensor,
+ SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION,
+ &minfo->sensor_firmware_version);
+
+ /* SMIA */
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION,
+ &minfo->smia_version);
+ if (!rval)
+ rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION,
+ &minfo->smiapp_version);
+
+ if (rval) {
+ dev_err(&client->dev, "sensor detection failed\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n",
+ minfo->manufacturer_id, minfo->model_id);
+
+ dev_dbg(&client->dev,
+ "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n",
+ minfo->revision_number_major, minfo->revision_number_minor,
+ minfo->module_year, minfo->module_month, minfo->module_day);
+
+ dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n",
+ minfo->sensor_manufacturer_id, minfo->sensor_model_id);
+
+ dev_dbg(&client->dev,
+ "sensor revision 0x%2.2x firmware version 0x%2.2x\n",
+ minfo->sensor_revision_number, minfo->sensor_firmware_version);
+
+ dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n",
+ minfo->smia_version, minfo->smiapp_version);
+
+ /*
+ * Some modules have bad data in the lvalues below. Hope the
+ * rvalues have better stuff. The lvalues are module
+ * parameters whereas the rvalues are sensor parameters.
+ */
+ if (!minfo->manufacturer_id && !minfo->model_id) {
+ minfo->manufacturer_id = minfo->sensor_manufacturer_id;
+ minfo->model_id = minfo->sensor_model_id;
+ minfo->revision_number_major = minfo->sensor_revision_number;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) {
+ if (smiapp_module_idents[i].manufacturer_id
+ != minfo->manufacturer_id)
+ continue;
+ if (smiapp_module_idents[i].model_id != minfo->model_id)
+ continue;
+ if (smiapp_module_idents[i].flags
+ & SMIAPP_MODULE_IDENT_FLAG_REV_LE) {
+ if (smiapp_module_idents[i].revision_number_major
+ < minfo->revision_number_major)
+ continue;
+ } else {
+ if (smiapp_module_idents[i].revision_number_major
+ != minfo->revision_number_major)
+ continue;
+ }
+
+ minfo->name = smiapp_module_idents[i].name;
+ minfo->quirk = smiapp_module_idents[i].quirk;
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(smiapp_module_idents))
+ dev_warn(&client->dev,
+ "no quirks for this module; let's hope it's fully compliant\n");
+
+ dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n",
+ minfo->name, minfo->manufacturer_id, minfo->model_id,
+ minfo->revision_number_major);
+
+ strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name));
+
+ return 0;
+}
+
+static const struct v4l2_subdev_ops smiapp_ops;
+static const struct v4l2_subdev_internal_ops smiapp_internal_ops;
+static const struct media_entity_operations smiapp_entity_ops;
+
+static int smiapp_registered(struct v4l2_subdev *subdev)
+{
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ struct i2c_client *client = v4l2_get_subdevdata(subdev);
+ struct smiapp_subdev *last = NULL;
+ u32 tmp;
+ unsigned int i;
+ int rval;
+
+ sensor->vana = regulator_get(&client->dev, "VANA");
+ if (IS_ERR(sensor->vana)) {
+ dev_err(&client->dev, "could not get regulator for vana\n");
+ return -ENODEV;
+ }
+
+ if (!sensor->platform_data->set_xclk) {
+ sensor->ext_clk = clk_get(&client->dev,
+ sensor->platform_data->ext_clk_name);
+ if (IS_ERR(sensor->ext_clk)) {
+ dev_err(&client->dev, "could not get clock %s\n",
+ sensor->platform_data->ext_clk_name);
+ rval = -ENODEV;
+ goto out_clk_get;
+ }
+
+ rval = clk_set_rate(sensor->ext_clk,
+ sensor->platform_data->ext_clk);
+ if (rval < 0) {
+ dev_err(&client->dev,
+ "unable to set clock %s freq to %u\n",
+ sensor->platform_data->ext_clk_name,
+ sensor->platform_data->ext_clk);
+ rval = -ENODEV;
+ goto out_clk_set_rate;
+ }
+ }
+
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) {
+ if (gpio_request_one(sensor->platform_data->xshutdown, 0,
+ "SMIA++ xshutdown") != 0) {
+ dev_err(&client->dev,
+ "unable to acquire reset gpio %d\n",
+ sensor->platform_data->xshutdown);
+ rval = -ENODEV;
+ goto out_clk_set_rate;
+ }
+ }
+
+ rval = smiapp_power_on(sensor);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_smiapp_power_on;
+ }
+
+ rval = smiapp_identify_module(subdev);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+
+ rval = smiapp_get_all_limits(sensor);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+
+ /*
+ * Handle Sensor Module orientation on the board.
+ *
+ * The application of H-FLIP and V-FLIP on the sensor is modified by
+ * the sensor orientation on the board.
+ *
+ * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set
+ * both H-FLIP and V-FLIP for normal operation which also implies
+ * that a set/unset operation for user space HFLIP and VFLIP v4l2
+ * controls will need to be internally inverted.
+ *
+ * Rotation also changes the bayer pattern.
+ */
+ if (sensor->platform_data->module_board_orient ==
+ SMIAPP_MODULE_BOARD_ORIENT_180)
+ sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP |
+ SMIAPP_IMAGE_ORIENTATION_VFLIP;
+
+ rval = smiapp_get_mbus_formats(sensor);
+ if (rval) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+
+ if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) {
+ u32 val;
+
+ rval = smiapp_read(sensor,
+ SMIAPP_REG_U8_BINNING_SUBTYPES, &val);
+ if (rval < 0) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+ sensor->nbinning_subtypes = min_t(u8, val,
+ SMIAPP_BINNING_SUBTYPES);
+
+ for (i = 0; i < sensor->nbinning_subtypes; i++) {
+ rval = smiapp_read(
+ sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val);
+ if (rval < 0) {
+ rval = -ENODEV;
+ goto out_power_off;
+ }
+ sensor->binning_subtypes[i] =
+ *(struct smiapp_binning_subtype *)&val;
+
+ dev_dbg(&client->dev, "binning %xx%x\n",
+ sensor->binning_subtypes[i].horizontal,
+ sensor->binning_subtypes[i].vertical);
+ }
+ }
+ sensor->binning_horizontal = 1;
+ sensor->binning_vertical = 1;
+
+ /* SMIA++ NVM initialization - it will be read from the sensor
+ * when it is first requested by userspace.
+ */
+ if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) {
+ sensor->nvm = kzalloc(sensor->platform_data->nvm_size,
+ GFP_KERNEL);
+ if (sensor->nvm == NULL) {
+ dev_err(&client->dev, "nvm buf allocation failed\n");
+ rval = -ENOMEM;
+ goto out_power_off;
+ }
+
+ if (device_create_file(&client->dev, &dev_attr_nvm) != 0) {
+ dev_err(&client->dev, "sysfs nvm entry failed\n");
+ rval = -EBUSY;
+ goto out_power_off;
+ }
+ }
+
+ rval = smiapp_call_quirk(sensor, limits);
+ if (rval) {
+ dev_err(&client->dev, "limits quirks failed\n");
+ goto out_nvm_release;
+ }
+
+ /* We consider this as profile 0 sensor if any of these are zero. */
+ if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] ||
+ !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] ||
+ !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] ||
+ !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) {
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0;
+ } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ != SMIAPP_SCALING_CAPABILITY_NONE) {
+ if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY]
+ == SMIAPP_SCALING_CAPABILITY_HORIZONTAL)
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1;
+ else
+ sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2;
+ sensor->scaler = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+ } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY]
+ == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) {
+ sensor->scaler = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+ }
+ sensor->binner = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+ sensor->pixel_array = &sensor->ssds[sensor->ssds_used];
+ sensor->ssds_used++;
+
+ sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN];
+
+ for (i = 0; i < SMIAPP_SUBDEVS; i++) {
+ struct {
+ struct smiapp_subdev *ssd;
+ char *name;
+ } const __this[] = {
+ { sensor->scaler, "scaler", },
+ { sensor->binner, "binner", },
+ { sensor->pixel_array, "pixel array", },
+ }, *_this = &__this[i];
+ struct smiapp_subdev *this = _this->ssd;
+
+ if (!this)
+ continue;
+
+ if (this != sensor->src)
+ v4l2_subdev_init(&this->sd, &smiapp_ops);
+
+ this->sensor = sensor;
+
+ if (this == sensor->pixel_array) {
+ this->npads = 1;
+ } else {
+ this->npads = 2;
+ this->source_pad = 1;
+ }
+
+ snprintf(this->sd.name,
+ sizeof(this->sd.name), "%s %s",
+ sensor->minfo.name, _this->name);
+
+ this->sink_fmt.width =
+ sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
+ this->sink_fmt.height =
+ sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
+ this->compose.width = this->sink_fmt.width;
+ this->compose.height = this->sink_fmt.height;
+ this->crop[this->source_pad] = this->compose;
+ this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE;
+ if (this != sensor->pixel_array) {
+ this->crop[this->sink_pad] = this->compose;
+ this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK;
+ }
+
+ this->sd.entity.ops = &smiapp_entity_ops;
+
+ if (last == NULL) {
+ last = this;
+ continue;
+ }
+
+ this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ this->sd.internal_ops = &smiapp_internal_ops;
+ this->sd.owner = NULL;
+ v4l2_set_subdevdata(&this->sd, client);
+
+ rval = media_entity_init(&this->sd.entity,
+ this->npads, this->pads, 0);
+ if (rval) {
+ dev_err(&client->dev,
+ "media_entity_init failed\n");
+ goto out_nvm_release;
+ }
+
+ rval = media_entity_create_link(&this->sd.entity,
+ this->source_pad,
+ &last->sd.entity,
+ last->sink_pad,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (rval) {
+ dev_err(&client->dev,
+ "media_entity_create_link failed\n");
+ goto out_nvm_release;
+ }
+
+ rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev,
+ &this->sd);
+ if (rval) {
+ dev_err(&client->dev,
+ "v4l2_device_register_subdev failed\n");
+ goto out_nvm_release;
+ }
+
+ last = this;
+ }
+
+ dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile);
+
+ sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
+
+ /* final steps */
+ smiapp_read_frame_fmt(sensor);
+ rval = smiapp_init_controls(sensor);
+ if (rval < 0)
+ goto out_nvm_release;
+
+ rval = smiapp_update_mode(sensor);
+ if (rval) {
+ dev_err(&client->dev, "update mode failed\n");
+ goto out_nvm_release;
+ }
+
+ sensor->streaming = false;
+ sensor->dev_init_done = true;
+
+ /* check flash capability */
+ rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp);
+ sensor->flash_capability = tmp;
+ if (rval)
+ goto out_nvm_release;
+
+ smiapp_power_off(sensor);
+
+ return 0;
+
+out_nvm_release:
+ device_remove_file(&client->dev, &dev_attr_nvm);
+
+out_power_off:
+ kfree(sensor->nvm);
+ sensor->nvm = NULL;
+ smiapp_power_off(sensor);
+
+out_smiapp_power_on:
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_free(sensor->platform_data->xshutdown);
+
+out_clk_set_rate:
+ clk_put(sensor->ext_clk);
+ sensor->ext_clk = NULL;
+
+out_clk_get:
+ regulator_put(sensor->vana);
+ sensor->vana = NULL;
+ return rval;
+}
+
+static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ struct smiapp_subdev *ssd = to_smiapp_subdev(sd);
+ struct smiapp_sensor *sensor = ssd->sensor;
+ u32 mbus_code =
+ smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code;
+ unsigned int i;
+
+ mutex_lock(&sensor->mutex);
+
+ for (i = 0; i < ssd->npads; i++) {
+ struct v4l2_mbus_framefmt *try_fmt =
+ v4l2_subdev_get_try_format(fh, i);
+ struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i);
+ struct v4l2_rect *try_comp;
+
+ try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1;
+ try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1;
+ try_fmt->code = mbus_code;
+
+ try_crop->top = 0;
+ try_crop->left = 0;
+ try_crop->width = try_fmt->width;
+ try_crop->height = try_fmt->height;
+
+ if (ssd != sensor->pixel_array)
+ continue;
+
+ try_comp = v4l2_subdev_get_try_compose(fh, i);
+ *try_comp = *try_crop;
+ }
+
+ mutex_unlock(&sensor->mutex);
+
+ return smiapp_set_power(sd, 1);
+}
+
+static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+ return smiapp_set_power(sd, 0);
+}
+
+static const struct v4l2_subdev_video_ops smiapp_video_ops = {
+ .s_stream = smiapp_set_stream,
+};
+
+static const struct v4l2_subdev_core_ops smiapp_core_ops = {
+ .s_power = smiapp_set_power,
+};
+
+static const struct v4l2_subdev_pad_ops smiapp_pad_ops = {
+ .enum_mbus_code = smiapp_enum_mbus_code,
+ .get_fmt = smiapp_get_format,
+ .set_fmt = smiapp_set_format,
+ .get_selection = smiapp_get_selection,
+ .set_selection = smiapp_set_selection,
+};
+
+static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = {
+ .g_skip_frames = smiapp_get_skip_frames,
+};
+
+static const struct v4l2_subdev_ops smiapp_ops = {
+ .core = &smiapp_core_ops,
+ .video = &smiapp_video_ops,
+ .pad = &smiapp_pad_ops,
+ .sensor = &smiapp_sensor_ops,
+};
+
+static const struct media_entity_operations smiapp_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = {
+ .registered = smiapp_registered,
+ .open = smiapp_open,
+ .close = smiapp_close,
+};
+
+static const struct v4l2_subdev_internal_ops smiapp_internal_ops = {
+ .open = smiapp_open,
+ .close = smiapp_close,
+};
+
+/* -----------------------------------------------------------------------------
+ * I2C Driver
+ */
+
+#ifdef CONFIG_PM
+
+static int smiapp_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ bool streaming;
+
+ BUG_ON(mutex_is_locked(&sensor->mutex));
+
+ if (sensor->power_count == 0)
+ return 0;
+
+ if (sensor->streaming)
+ smiapp_stop_streaming(sensor);
+
+ streaming = sensor->streaming;
+
+ smiapp_power_off(sensor);
+
+ /* save state for resume */
+ sensor->streaming = streaming;
+
+ return 0;
+}
+
+static int smiapp_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ int rval;
+
+ if (sensor->power_count == 0)
+ return 0;
+
+ rval = smiapp_power_on(sensor);
+ if (rval)
+ return rval;
+
+ if (sensor->streaming)
+ rval = smiapp_start_streaming(sensor);
+
+ return rval;
+}
+
+#else
+
+#define smiapp_suspend NULL
+#define smiapp_resume NULL
+
+#endif /* CONFIG_PM */
+
+static int smiapp_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct smiapp_sensor *sensor;
+ int rval;
+
+ if (client->dev.platform_data == NULL)
+ return -ENODEV;
+
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL)
+ return -ENOMEM;
+
+ sensor->platform_data = client->dev.platform_data;
+ mutex_init(&sensor->mutex);
+ mutex_init(&sensor->power_mutex);
+ sensor->src = &sensor->ssds[sensor->ssds_used];
+
+ v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops);
+ sensor->src->sd.internal_ops = &smiapp_internal_src_ops;
+ sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->src->sensor = sensor;
+
+ sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+ rval = media_entity_init(&sensor->src->sd.entity, 2,
+ sensor->src->pads, 0);
+ if (rval < 0)
+ kfree(sensor);
+
+ return rval;
+}
+
+static int __exit smiapp_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct smiapp_sensor *sensor = to_smiapp_sensor(subdev);
+ unsigned int i;
+
+ if (sensor->power_count) {
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_set_value(sensor->platform_data->xshutdown, 0);
+ if (sensor->platform_data->set_xclk)
+ sensor->platform_data->set_xclk(&sensor->src->sd, 0);
+ else
+ clk_disable(sensor->ext_clk);
+ sensor->power_count = 0;
+ }
+
+ if (sensor->nvm) {
+ device_remove_file(&client->dev, &dev_attr_nvm);
+ kfree(sensor->nvm);
+ }
+
+ for (i = 0; i < sensor->ssds_used; i++) {
+ media_entity_cleanup(&sensor->ssds[i].sd.entity);
+ v4l2_device_unregister_subdev(&sensor->ssds[i].sd);
+ }
+ smiapp_free_controls(sensor);
+ if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN)
+ gpio_free(sensor->platform_data->xshutdown);
+ if (sensor->ext_clk)
+ clk_put(sensor->ext_clk);
+ if (sensor->vana)
+ regulator_put(sensor->vana);
+
+ kfree(sensor);
+
+ return 0;
+}
+
+static const struct i2c_device_id smiapp_id_table[] = {
+ { SMIAPP_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, smiapp_id_table);
+
+static const struct dev_pm_ops smiapp_pm_ops = {
+ .suspend = smiapp_suspend,
+ .resume = smiapp_resume,
+};
+
+static struct i2c_driver smiapp_i2c_driver = {
+ .driver = {
+ .name = SMIAPP_NAME,
+ .pm = &smiapp_pm_ops,
+ },
+ .probe = smiapp_probe,
+ .remove = __exit_p(smiapp_remove),
+ .id_table = smiapp_id_table,
+};
+
+module_i2c_driver(smiapp_i2c_driver);
+
+MODULE_AUTHOR("Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>");
+MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/smiapp/smiapp-limits.c b/drivers/media/video/smiapp/smiapp-limits.c
new file mode 100644
index 00000000000..0800e095724
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-limits.c
@@ -0,0 +1,132 @@
+/*
+ * drivers/media/video/smiapp/smiapp-limits.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 "smiapp.h"
+
+struct smiapp_reg_limits smiapp_reg_limits[] = {
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" },
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" },
+ { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" },
+ { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" },
+ { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */
+ { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" },
+ { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" },
+ { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */
+ { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" },
+ { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" },
+ { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" },
+ { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" },
+ { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */
+ { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" },
+ { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" },
+ { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" },
+ { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" },
+ { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */
+ { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" },
+ { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" },
+ { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" },
+ { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" },
+ { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */
+ { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" },
+ { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" },
+ { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" },
+ { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" },
+ { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */
+ { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" },
+ { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" },
+ { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" },
+ { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" },
+ { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */
+ { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" },
+ { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" },
+ { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" },
+ { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" },
+ { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */
+ { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" },
+ { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" },
+ { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" },
+ { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" },
+ { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */
+ { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" },
+ { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" },
+ { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" },
+ { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" },
+ { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */
+ { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" },
+ { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" },
+ { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" },
+ { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" },
+ { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */
+ { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" },
+ { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" },
+ { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" },
+ { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" },
+ { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */
+ { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" },
+ { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" },
+ { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" },
+ { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" },
+ { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */
+ { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" },
+ { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" },
+ { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" },
+ { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" },
+ { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */
+ { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" },
+ { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */
+ { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" },
+ { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" },
+ { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" },
+ { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" },
+ { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */
+ { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" },
+ { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" },
+ { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" },
+ { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */
+ { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" },
+ { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" },
+ { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" },
+ { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" },
+ { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */
+ { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" },
+ { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" },
+ { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" },
+ { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" },
+ { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */
+ { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" },
+ { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" },
+ { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" },
+ { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" },
+ { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */
+ { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" },
+ { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" },
+ { 0, NULL },
+};
diff --git a/drivers/media/video/smiapp/smiapp-limits.h b/drivers/media/video/smiapp/smiapp-limits.h
new file mode 100644
index 00000000000..7f4836bb78d
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-limits.h
@@ -0,0 +1,128 @@
+/*
+ * drivers/media/video/smiapp/smiapp-limits.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2
+#define SMIAPP_LIMIT_THS_ZERO_MIN 3
+#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4
+#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5
+#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6
+#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9
+#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10
+#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11
+#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12
+#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13
+#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14
+#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15
+#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16
+#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17
+#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18
+#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19
+#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20
+#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21
+#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22
+#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23
+#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24
+#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25
+#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26
+#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27
+#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28
+#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29
+#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30
+#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31
+#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32
+#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33
+#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34
+#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35
+#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36
+#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37
+#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38
+#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39
+#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40
+#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41
+#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42
+#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43
+#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44
+#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45
+#define SMIAPP_LIMIT_X_ADDR_MIN 46
+#define SMIAPP_LIMIT_Y_ADDR_MIN 47
+#define SMIAPP_LIMIT_X_ADDR_MAX 48
+#define SMIAPP_LIMIT_Y_ADDR_MAX 49
+#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50
+#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51
+#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52
+#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53
+#define SMIAPP_LIMIT_MIN_EVEN_INC 54
+#define SMIAPP_LIMIT_MAX_EVEN_INC 55
+#define SMIAPP_LIMIT_MIN_ODD_INC 56
+#define SMIAPP_LIMIT_MAX_ODD_INC 57
+#define SMIAPP_LIMIT_SCALING_CAPABILITY 58
+#define SMIAPP_LIMIT_SCALER_M_MIN 59
+#define SMIAPP_LIMIT_SCALER_M_MAX 60
+#define SMIAPP_LIMIT_SCALER_N_MIN 61
+#define SMIAPP_LIMIT_SCALER_N_MAX 62
+#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63
+#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64
+#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65
+#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66
+#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67
+#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68
+#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69
+#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70
+#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74
+#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75
+#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76
+#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77
+#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78
+#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79
+#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80
+#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82
+#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83
+#define SMIAPP_LIMIT_BINNING_CAPABILITY 84
+#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85
+#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86
+#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87
+#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88
+#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89
+#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90
+#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91
+#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92
+#define SMIAPP_LIMIT_EDOF_CAPABILITY 93
+#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94
+#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95
+#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96
+#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97
+#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98
+#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99
+#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100
+#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101
+#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102
+#define SMIAPP_LIMIT_LAST 103
diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c
new file mode 100644
index 00000000000..55e87950dce
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-quirk.c
@@ -0,0 +1,306 @@
+/*
+ * drivers/media/video/smiapp/smiapp-quirk.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 "smiapp.h"
+
+static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val)
+{
+ return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val);
+}
+
+static int smiapp_write_8s(struct smiapp_sensor *sensor,
+ struct smiapp_reg_8 *regs, int len)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ for (; len > 0; len--, regs++) {
+ rval = smiapp_write_8(sensor, regs->reg, regs->val);
+ if (rval < 0) {
+ dev_err(&client->dev,
+ "error %d writing reg 0x%4.4x, val 0x%2.2x",
+ rval, regs->reg, regs->val);
+ return rval;
+ }
+ }
+
+ return 0;
+}
+
+void smiapp_replace_limit(struct smiapp_sensor *sensor,
+ u32 limit, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+
+ dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n",
+ smiapp_reg_limits[limit].addr,
+ smiapp_reg_limits[limit].what, val, val);
+ sensor->limits[limit] = val;
+}
+
+int smiapp_replace_limit_at(struct smiapp_sensor *sensor,
+ u32 reg, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int i;
+
+ for (i = 0; smiapp_reg_limits[i].addr; i++) {
+ if ((smiapp_reg_limits[i].addr & 0xffff) != reg)
+ continue;
+
+ smiapp_replace_limit(sensor, i, val);
+
+ return 0;
+ }
+
+ dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg);
+
+ return -EINVAL;
+}
+
+bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
+ u32 reg, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ const struct smia_reg *sreg;
+
+ if (!sensor->minfo.quirk)
+ return false;
+
+ sreg = sensor->minfo.quirk->regs;
+
+ if (!sreg)
+ return false;
+
+ while (sreg->type) {
+ u16 type = reg >> 16;
+ u16 reg16 = reg;
+
+ if (sreg->type != type || sreg->reg != reg16) {
+ sreg++;
+ continue;
+ }
+
+ switch ((u8)type) {
+ case SMIA_REG_8BIT:
+ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n",
+ reg, sreg->val);
+ break;
+ case SMIA_REG_16BIT:
+ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n",
+ reg, sreg->val);
+ break;
+ case SMIA_REG_32BIT:
+ dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n",
+ reg, sreg->val);
+ break;
+ }
+
+ *val = sreg->val;
+
+ return true;
+ }
+
+ return false;
+}
+
+static int jt8ew9_limits(struct smiapp_sensor *sensor)
+{
+ if (sensor->minfo.revision_number_major < 0x03)
+ sensor->frame_skip = 1;
+
+ /* Below 24 gain doesn't have effect at all, */
+ /* but ~59 is needed for full dynamic range */
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59);
+ smiapp_replace_limit(
+ sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000);
+
+ return 0;
+}
+
+static int jt8ew9_post_poweron(struct smiapp_sensor *sensor)
+{
+ struct smiapp_reg_8 regs[] = {
+ { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */
+ { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
+ { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */
+ { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */
+ { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
+ { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
+ { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */
+ { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */
+ { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */
+ { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */
+ { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */
+ { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */
+ { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */
+ { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */
+ { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */
+ /* Taken from v03. No idea what the rest are. */
+ { 0x32e0, 0x05 },
+ { 0x32e1, 0x05 },
+ { 0x32e2, 0x04 },
+ { 0x32e5, 0x04 },
+ { 0x32e6, 0x04 },
+
+ };
+
+ return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
+}
+
+const struct smiapp_quirk smiapp_jt8ew9_quirk = {
+ .limits = jt8ew9_limits,
+ .post_poweron = jt8ew9_post_poweron,
+};
+
+static int imx125es_post_poweron(struct smiapp_sensor *sensor)
+{
+ /* Taken from v02. No idea what the other two are. */
+ struct smiapp_reg_8 regs[] = {
+ /*
+ * 0x3302: clk during frame blanking:
+ * 0x00 - HS mode, 0x01 - LP11
+ */
+ { 0x3302, 0x01 },
+ { 0x302d, 0x00 },
+ { 0x3b08, 0x8c },
+ };
+
+ return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
+}
+
+const struct smiapp_quirk smiapp_imx125es_quirk = {
+ .post_poweron = imx125es_post_poweron,
+};
+
+static int jt8ev1_limits(struct smiapp_sensor *sensor)
+{
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271);
+ smiapp_replace_limit(sensor,
+ SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184);
+
+ return 0;
+}
+
+static int jt8ev1_post_poweron(struct smiapp_sensor *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ int rval;
+
+ struct smiapp_reg_8 regs[] = {
+ { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */
+ { 0x30a3, 0xd0 }, /* FLASH STROBE enable */
+ { 0x3237, 0x00 }, /* For control of pulse timing for ADC */
+ { 0x3238, 0x43 },
+ { 0x3301, 0x06 }, /* For analog bias for sensor */
+ { 0x3302, 0x06 },
+ { 0x3304, 0x00 },
+ { 0x3305, 0x88 },
+ { 0x332a, 0x14 },
+ { 0x332c, 0x6b },
+ { 0x3336, 0x01 },
+ { 0x333f, 0x1f },
+ { 0x3355, 0x00 },
+ { 0x3356, 0x20 },
+ { 0x33bf, 0x20 }, /* Adjust the FBC speed */
+ { 0x33c9, 0x20 },
+ { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */
+ { 0x33cf, 0xec }, /* For Black sun */
+ { 0x3328, 0x80 }, /* Ugh. No idea what's this. */
+ };
+
+ struct smiapp_reg_8 regs_96[] = {
+ { 0x30ae, 0x00 }, /* For control of ADC clock */
+ { 0x30af, 0xd0 },
+ { 0x30b0, 0x01 },
+ };
+
+ rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs));
+ if (rval < 0)
+ return rval;
+
+ switch (sensor->platform_data->ext_clk) {
+ case 9600000:
+ return smiapp_write_8s(sensor, regs_96,
+ ARRAY_SIZE(regs_96));
+ default:
+ dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n",
+ sensor->platform_data->ext_clk);
+ return 0;
+ }
+}
+
+static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor)
+{
+ return smiapp_write_8(sensor, 0x3328, 0x00);
+}
+
+static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor)
+{
+ int rval;
+
+ /* Workaround: allows fast standby to work properly */
+ rval = smiapp_write_8(sensor, 0x3205, 0x04);
+ if (rval < 0)
+ return rval;
+
+ /* Wait for 1 ms + one line => 2 ms is likely enough */
+ usleep_range(2000, 2000);
+
+ /* Restore it */
+ rval = smiapp_write_8(sensor, 0x3205, 0x00);
+ if (rval < 0)
+ return rval;
+
+ return smiapp_write_8(sensor, 0x3328, 0x80);
+}
+
+const struct smiapp_quirk smiapp_jt8ev1_quirk = {
+ .limits = jt8ev1_limits,
+ .post_poweron = jt8ev1_post_poweron,
+ .pre_streamon = jt8ev1_pre_streamon,
+ .post_streamoff = jt8ev1_post_streamoff,
+ .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE,
+};
+
+static int tcm8500md_limits(struct smiapp_sensor *sensor)
+{
+ smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000);
+
+ return 0;
+}
+
+const struct smiapp_quirk smiapp_tcm8500md_quirk = {
+ .limits = tcm8500md_limits,
+};
diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h
new file mode 100644
index 00000000000..f4dcaabaefe
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-quirk.h
@@ -0,0 +1,83 @@
+/*
+ * drivers/media/video/smiapp/smiapp-quirk.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 __SMIAPP_QUIRK__
+#define __SMIAPP_QUIRK__
+
+struct smiapp_sensor;
+
+/**
+ * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard
+ *
+ * @limits: Replace sensor->limits with values which can't be read from
+ * sensor registers. Called the first time the sensor is powered up.
+ * @post_poweron: Called always after the sensor has been fully powered on.
+ * @pre_streamon: Called just before streaming is enabled.
+ * @post_streamon: Called right after stopping streaming.
+ */
+struct smiapp_quirk {
+ int (*limits)(struct smiapp_sensor *sensor);
+ int (*post_poweron)(struct smiapp_sensor *sensor);
+ int (*pre_streamon)(struct smiapp_sensor *sensor);
+ int (*post_streamoff)(struct smiapp_sensor *sensor);
+ const struct smia_reg *regs;
+ unsigned long flags;
+};
+
+/* op pix clock is for all lanes in total normally */
+#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0)
+#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1)
+
+struct smiapp_reg_8 {
+ u16 reg;
+ u8 val;
+};
+
+void smiapp_replace_limit(struct smiapp_sensor *sensor,
+ u32 limit, u32 val);
+bool smiapp_quirk_reg(struct smiapp_sensor *sensor,
+ u32 reg, u32 *val);
+
+#define SMIAPP_MK_QUIRK_REG(_reg, _val) \
+ { \
+ .type = (_reg >> 16), \
+ .reg = (u16)_reg, \
+ .val = _val, \
+ }
+
+#define smiapp_call_quirk(_sensor, _quirk, ...) \
+ (_sensor->minfo.quirk && \
+ _sensor->minfo.quirk->_quirk ? \
+ _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0)
+
+#define smiapp_needs_quirk(_sensor, _quirk) \
+ (_sensor->minfo.quirk ? \
+ _sensor->minfo.quirk->flags & _quirk : 0)
+
+extern const struct smiapp_quirk smiapp_jt8ev1_quirk;
+extern const struct smiapp_quirk smiapp_imx125es_quirk;
+extern const struct smiapp_quirk smiapp_jt8ew9_quirk;
+extern const struct smiapp_quirk smiapp_tcm8500md_quirk;
+
+#endif /* __SMIAPP_QUIRK__ */
diff --git a/drivers/media/video/smiapp/smiapp-reg-defs.h b/drivers/media/video/smiapp/smiapp-reg-defs.h
new file mode 100644
index 00000000000..a089eb8161e
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-reg-defs.h
@@ -0,0 +1,503 @@
+/*
+ * drivers/media/video/smiapp/smiapp-reg-defs.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r))
+#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r))
+#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r))
+
+#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r))
+
+#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000)
+#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002)
+#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003)
+#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004)
+#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005)
+#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006)
+#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008)
+#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c)
+#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010)
+#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011)
+#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012)
+#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013)
+#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014)
+#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015)
+#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016)
+#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018)
+#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019)
+#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a)
+#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c)
+#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040)
+#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041)
+#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */
+#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092)
+#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0)
+#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1)
+#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1))
+#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100)
+#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101)
+#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103)
+#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104)
+#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105)
+#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106)
+#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107)
+#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108)
+#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109)
+#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110)
+#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111)
+#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112)
+#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114)
+#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115)
+#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116)
+#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117)
+#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118)
+#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119)
+#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a)
+#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b)
+#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c)
+#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d)
+#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e)
+#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120)
+#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130)
+#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132)
+#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134)
+#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136)
+#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138)
+#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139)
+#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200)
+#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a)
+#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214)
+#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300)
+#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302)
+#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304)
+#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306)
+#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308)
+#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a)
+#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340)
+#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342)
+#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344)
+#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346)
+#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348)
+#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a)
+#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c)
+#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e)
+#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380)
+#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382)
+#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384)
+#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386)
+#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400)
+#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402)
+#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404)
+#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406)
+#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408)
+#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a)
+#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c)
+#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e)
+#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500)
+#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600)
+#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602)
+#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604)
+#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606)
+#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608)
+#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a)
+#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c)
+#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e)
+#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610)
+#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700)
+#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800)
+#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801)
+#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802)
+#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803)
+#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804)
+#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805)
+#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806)
+#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807)
+#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808)
+#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820)
+#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900)
+#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901)
+#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86)
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87)
+#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00)
+#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01)
+#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02)
+#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03)
+#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04)
+#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05)
+#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06)
+#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07)
+#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08)
+#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09)
+#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a)
+#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d)
+#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e)
+#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f)
+#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10)
+#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11)
+#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12)
+#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13)
+#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14)
+#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15)
+#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16)
+#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80)
+#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83)
+#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85)
+#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86)
+#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88)
+#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a)
+#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92)
+#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94)
+#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0)
+#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8)
+#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca)
+#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00)
+#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01)
+#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02)
+#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03)
+#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04)
+#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06)
+#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08)
+#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a)
+#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e)
+#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10)
+#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12)
+#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14)
+#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18)
+#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a)
+#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b)
+#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c)
+#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d)
+#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e)
+#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20)
+#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22)
+#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24)
+#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28)
+#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a)
+#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c)
+#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e)
+#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80)
+#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82)
+#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84)
+#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86)
+#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88)
+#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a)
+#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c)
+#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e)
+#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00)
+#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01)
+#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02)
+#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03)
+#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86)
+#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88)
+#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89)
+#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a)
+#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00)
+#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01)
+#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02)
+#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10)
+#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff)
+#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000)
+#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004)
+#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086)
+#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088)
+#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100)
+#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104)
+#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108)
+#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a)
+#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c)
+#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110)
+#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114)
+#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116)
+#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118)
+#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c)
+#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120)
+#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122)
+#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124)
+#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128)
+#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c)
+#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130)
+#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134)
+#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136)
+#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140)
+#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142)
+#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144)
+#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146)
+#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148)
+#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a)
+#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c)
+#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160)
+#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162)
+#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164)
+#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168)
+#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c)
+#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e)
+#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170)
+#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174)
+#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180)
+#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182)
+#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184)
+#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186)
+#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188)
+#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a)
+#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c)
+#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e)
+#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0)
+#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2)
+#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4)
+#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6)
+#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200)
+#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204)
+#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206)
+#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208)
+#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a)
+#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c)
+#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e)
+#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e)
+#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410)
+#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500)
+#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502)
+#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600)
+#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601)
+#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602)
+#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603)
+#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610)
+#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614)
+#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618)
+#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700)
+#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702)
+#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704)
+#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706)
+#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a)
+#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c)
+#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710)
+#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711)
+#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712)
+#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */
+#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800)
+#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900)
+#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901)
+#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902)
+#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903)
+#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904)
+#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906)
+#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980)
+#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981)
+#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982)
+#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983)
+#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984)
+#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985)
+#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986)
+#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987)
+#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988)
+#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0)
+#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1)
+#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2)
+#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4)
+#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00)
+#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02)
+#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02)
+#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04)
+#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40)
+#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42)
+#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44)
+#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00)
+#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01)
+#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02)
diff --git a/drivers/media/video/smiapp/smiapp-reg.h b/drivers/media/video/smiapp/smiapp-reg.h
new file mode 100644
index 00000000000..d0167aa1753
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-reg.h
@@ -0,0 +1,122 @@
+/*
+ * drivers/media/video/smiapp/smiapp-reg.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 __SMIAPP_REG_H_
+#define __SMIAPP_REG_H_
+
+#include "smiapp-reg-defs.h"
+
+/* Bits for above register */
+#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0)
+#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1)
+
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0)
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1)
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1)
+#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2)
+#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3)
+
+#define SMIAPP_SOFTWARE_RESET (1 << 0)
+
+#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0)
+#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1)
+
+#define SMIAPP_DPHY_CTRL_AUTOMATIC 0
+/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */
+#define SMIAPP_DPHY_CTRL_UI 1
+#define SMIAPP_DPHY_CTRL_REGISTER 2
+
+#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1
+#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2
+
+#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0
+#define SMIAPP_MODE_SELECT_STREAMING 1
+
+#define SMIAPP_SCALING_MODE_NONE 0
+#define SMIAPP_SCALING_MODE_HORIZONTAL 1
+#define SMIAPP_SCALING_MODE_BOTH 2
+
+#define SMIAPP_SCALING_CAPABILITY_NONE 0
+#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1
+#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */
+
+/* digital crop right before scaler */
+#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0
+#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1
+
+#define SMIAPP_BINNING_CAPABILITY_NO 0
+#define SMIAPP_BINNING_CAPABILITY_YES 1
+
+/* Maximum number of binning subtypes */
+#define SMIAPP_BINNING_SUBTYPES 253
+
+#define SMIAPP_PIXEL_ORDER_GRBG 0
+#define SMIAPP_PIXEL_ORDER_RGGB 1
+#define SMIAPP_PIXEL_ORDER_BGGR 2
+#define SMIAPP_PIXEL_ORDER_GBRG 3
+
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8
+#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16
+
+#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01
+#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02
+#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f
+#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0
+#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4
+
+#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000
+#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12
+#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff
+
+#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000
+#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28
+#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff
+
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4
+#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5
+
+#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0
+#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1
+
+/* Scaling N factor */
+#define SMIAPP_SCALE_N 16
+
+/* Image statistics registers */
+/* Registers 0x2000 to 0x2fff are reserved for future
+ * use for statistics features.
+ */
+
+/* Manufacturer Specific Registers: 0x3000 to 0x3fff
+ * The manufacturer specifies these as a black box.
+ */
+
+#endif /* __SMIAPP_REG_H_ */
diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c
new file mode 100644
index 00000000000..b1812b17a40
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-regs.c
@@ -0,0 +1,273 @@
+/*
+ * drivers/media/video/smiapp/smiapp-regs.c
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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/i2c.h>
+
+#include "smiapp.h"
+#include "smiapp-regs.h"
+
+static uint32_t float_to_u32_mul_1000000(struct i2c_client *client,
+ uint32_t phloat)
+{
+ int32_t exp;
+ uint64_t man;
+
+ if (phloat >= 0x80000000) {
+ dev_err(&client->dev, "this is a negative number\n");
+ return 0;
+ }
+
+ if (phloat == 0x7f800000)
+ return ~0; /* Inf. */
+
+ if ((phloat & 0x7f800000) == 0x7f800000) {
+ dev_err(&client->dev, "NaN or other special number\n");
+ return 0;
+ }
+
+ /* Valid cases begin here */
+ if (phloat == 0)
+ return 0; /* Valid zero */
+
+ if (phloat > 0x4f800000)
+ return ~0; /* larger than 4294967295 */
+
+ /*
+ * Unbias exponent (note how phloat is now guaranteed to
+ * have 0 in the high bit)
+ */
+ exp = ((int32_t)phloat >> 23) - 127;
+
+ /* Extract mantissa, add missing '1' bit and it's in MHz */
+ man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL;
+
+ if (exp < 0)
+ man >>= -exp;
+ else
+ man <<= exp;
+
+ man >>= 23; /* Remove mantissa bias */
+
+ return man & 0xffffffff;
+}
+
+
+/*
+ * Read a 8/16/32-bit i2c register. The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg,
+ u16 len, u32 *val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct i2c_msg msg;
+ unsigned char data[4];
+ u16 offset = reg;
+ int r;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = 2;
+ msg.buf = data;
+
+ /* high byte goes out first */
+ data[0] = (u8) (offset >> 8);
+ data[1] = (u8) offset;
+ r = i2c_transfer(client->adapter, &msg, 1);
+ if (r != 1) {
+ if (r >= 0)
+ r = -EBUSY;
+ goto err;
+ }
+
+ msg.len = len;
+ msg.flags = I2C_M_RD;
+ r = i2c_transfer(client->adapter, &msg, 1);
+ if (r != 1) {
+ if (r >= 0)
+ r = -EBUSY;
+ goto err;
+ }
+
+ *val = 0;
+ /* high byte comes first */
+ switch (len) {
+ case SMIA_REG_32BIT:
+ *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) +
+ data[3];
+ break;
+ case SMIA_REG_16BIT:
+ *val = (data[0] << 8) + data[1];
+ break;
+ case SMIA_REG_8BIT:
+ *val = data[0];
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+
+err:
+ dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r);
+
+ return r;
+}
+
+/* Read a register using 8-bit access only. */
+static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg,
+ u16 len, u32 *val)
+{
+ unsigned int i;
+ int rval;
+
+ *val = 0;
+
+ for (i = 0; i < len; i++) {
+ u32 val8;
+
+ rval = ____smiapp_read(sensor, reg + i, 1, &val8);
+ if (rval < 0)
+ return rval;
+ *val |= val8 << ((len - i - 1) << 3);
+ }
+
+ return 0;
+}
+
+/*
+ * Read a 8/16/32-bit i2c register. The value is returned in 'val'.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val,
+ bool only8)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ unsigned int len = (u8)(reg >> 16);
+ int rval;
+
+ if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT
+ && len != SMIA_REG_32BIT)
+ return -EINVAL;
+
+ if (smiapp_quirk_reg(sensor, reg, val))
+ goto found_quirk;
+
+ if (len == SMIA_REG_8BIT && !only8)
+ rval = ____smiapp_read(sensor, (u16)reg, len, val);
+ else
+ rval = ____smiapp_read_8only(sensor, (u16)reg, len, val);
+ if (rval < 0)
+ return rval;
+
+found_quirk:
+ if (reg & SMIA_REG_FLAG_FLOAT)
+ *val = float_to_u32_mul_1000000(client, *val);
+
+ return 0;
+}
+
+int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+{
+ return __smiapp_read(
+ sensor, reg, val,
+ smiapp_needs_quirk(sensor,
+ SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY));
+}
+
+int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val)
+{
+ return __smiapp_read(sensor, reg, val, true);
+}
+
+/*
+ * Write to a 8/16-bit register.
+ * Returns zero if successful, or non-zero otherwise.
+ */
+int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd);
+ struct i2c_msg msg;
+ unsigned char data[6];
+ unsigned int retries;
+ unsigned int flags = reg >> 24;
+ unsigned int len = (u8)(reg >> 16);
+ u16 offset = reg;
+ int r;
+
+ if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT &&
+ len != SMIA_REG_32BIT) || flags)
+ return -EINVAL;
+
+ msg.addr = client->addr;
+ msg.flags = 0; /* Write */
+ msg.len = 2 + len;
+ msg.buf = data;
+
+ /* high byte goes out first */
+ data[0] = (u8) (reg >> 8);
+ data[1] = (u8) (reg & 0xff);
+
+ switch (len) {
+ case SMIA_REG_8BIT:
+ data[2] = val;
+ break;
+ case SMIA_REG_16BIT:
+ data[2] = val >> 8;
+ data[3] = val;
+ break;
+ case SMIA_REG_32BIT:
+ data[2] = val >> 24;
+ data[3] = val >> 16;
+ data[4] = val >> 8;
+ data[5] = val;
+ break;
+ default:
+ BUG();
+ }
+
+ for (retries = 0; retries < 5; retries++) {
+ /*
+ * Due to unknown reason sensor stops responding. This
+ * loop is a temporaty solution until the root cause
+ * is found.
+ */
+ r = i2c_transfer(client->adapter, &msg, 1);
+ if (r == 1) {
+ if (retries)
+ dev_err(&client->dev,
+ "sensor i2c stall encountered. "
+ "retries: %d\n", retries);
+ return 0;
+ }
+
+ usleep_range(2000, 2000);
+ }
+
+ dev_err(&client->dev,
+ "wrote 0x%x to offset 0x%x error %d\n", val, offset, r);
+
+ return r;
+}
diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h
new file mode 100644
index 00000000000..7f9013b4797
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp-regs.h
@@ -0,0 +1,49 @@
+/*
+ * include/media/smiapp/smiapp-regs.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2011--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 SMIAPP_REGS_H
+#define SMIAPP_REGS_H
+
+#include <linux/i2c.h>
+#include <linux/types.h>
+
+/* Use upper 8 bits of the type field for flags */
+#define SMIA_REG_FLAG_FLOAT (1 << 24)
+
+#define SMIA_REG_8BIT 1
+#define SMIA_REG_16BIT 2
+#define SMIA_REG_32BIT 4
+struct smia_reg {
+ u16 type;
+ u16 reg; /* 16-bit offset */
+ u32 val; /* 8/16/32-bit value */
+};
+
+struct smiapp_sensor;
+
+int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val);
+int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val);
+int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val);
+
+#endif
diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h
new file mode 100644
index 00000000000..587f7f11238
--- /dev/null
+++ b/drivers/media/video/smiapp/smiapp.h
@@ -0,0 +1,252 @@
+/*
+ * drivers/media/video/smiapp/smiapp.h
+ *
+ * Generic driver for SMIA/SMIA++ compliant camera modules
+ *
+ * Copyright (C) 2010--2012 Nokia Corporation
+ * Contact: Sakari Ailus <sakari.ailus@maxwell.research.nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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 __SMIAPP_PRIV_H_
+#define __SMIAPP_PRIV_H_
+
+#include <linux/mutex.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/smiapp.h>
+
+#include "smiapp-pll.h"
+#include "smiapp-reg.h"
+#include "smiapp-regs.h"
+#include "smiapp-quirk.h"
+
+/*
+ * Standard SMIA++ constants
+ */
+#define SMIA_VERSION_1 10
+#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */
+#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */
+#define SMIAPP_VERSION_1 10
+
+#define SMIAPP_PROFILE_0 0
+#define SMIAPP_PROFILE_1 1
+#define SMIAPP_PROFILE_2 2
+
+#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */
+
+#define SMIAPP_RESET_DELAY_CLOCKS 2400
+#define SMIAPP_RESET_DELAY(clk) \
+ (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \
+ + (clk) / 1000 - 1) / ((clk) / 1000))
+
+#include "smiapp-limits.h"
+
+struct smiapp_quirk;
+
+#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0)
+
+struct smiapp_module_ident {
+ u8 manufacturer_id;
+ u16 model_id;
+ u8 revision_number_major;
+
+ u8 flags;
+
+ char *name;
+ const struct smiapp_quirk *quirk;
+};
+
+struct smiapp_module_info {
+ u32 manufacturer_id;
+ u32 model_id;
+ u32 revision_number_major;
+ u32 revision_number_minor;
+
+ u32 module_year;
+ u32 module_month;
+ u32 module_day;
+
+ u32 sensor_manufacturer_id;
+ u32 sensor_model_id;
+ u32 sensor_revision_number;
+ u32 sensor_firmware_version;
+
+ u32 smia_version;
+ u32 smiapp_version;
+
+ u32 smiapp_profile;
+
+ char *name;
+ const struct smiapp_quirk *quirk;
+};
+
+#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = fl, \
+ .name = _name, \
+ .quirk = _quirk, }
+
+#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \
+ .name = _name, \
+ .quirk = _quirk, }
+
+#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \
+ .name = _name, }
+
+#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = 0, \
+ .name = _name, \
+ .quirk = _quirk, }
+
+#define SMIAPP_IDENT(manufacturer, model, rev, _name) \
+ { .manufacturer_id = manufacturer, \
+ .model_id = model, \
+ .revision_number_major = rev, \
+ .flags = 0, \
+ .name = _name, }
+
+struct smiapp_reg_limits {
+ u32 addr;
+ char *what;
+};
+
+extern struct smiapp_reg_limits smiapp_reg_limits[];
+
+struct smiapp_csi_data_format {
+ u32 code;
+ u8 width;
+ u8 compressed;
+ u8 pixel_order;
+};
+
+#define SMIAPP_SUBDEVS 3
+
+#define SMIAPP_PA_PAD_SRC 0
+#define SMIAPP_PAD_SINK 0
+#define SMIAPP_PAD_SRC 1
+#define SMIAPP_PADS 2
+
+struct smiapp_binning_subtype {
+ u8 horizontal:4;
+ u8 vertical:4;
+} __packed;
+
+struct smiapp_subdev {
+ struct v4l2_subdev sd;
+ struct media_pad pads[2];
+ struct v4l2_rect sink_fmt;
+ struct v4l2_rect crop[2];
+ struct v4l2_rect compose; /* compose on sink */
+ unsigned short sink_pad;
+ unsigned short source_pad;
+ int npads;
+ struct smiapp_sensor *sensor;
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+/*
+ * struct smiapp_sensor - Main device structure
+ */
+struct smiapp_sensor {
+ /*
+ * "mutex" is used to serialise access to all fields here
+ * except v4l2_ctrls at the end of the struct. "mutex" is also
+ * used to serialise access to file handle specific
+ * information. The exception to this rule is the power_mutex
+ * below.
+ */
+ struct mutex mutex;
+ /*
+ * power_mutex is used to serialise power management related
+ * activities. Acquiring "mutex" at that time isn't necessary
+ * since there are no other users anyway.
+ */
+ struct mutex power_mutex;
+ struct smiapp_subdev ssds[SMIAPP_SUBDEVS];
+ u32 ssds_used;
+ struct smiapp_subdev *src;
+ struct smiapp_subdev *binner;
+ struct smiapp_subdev *scaler;
+ struct smiapp_subdev *pixel_array;
+ struct smiapp_platform_data *platform_data;
+ struct regulator *vana;
+ struct clk *ext_clk;
+ u32 limits[SMIAPP_LIMIT_LAST];
+ u8 nbinning_subtypes;
+ struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES];
+ u32 mbus_frame_fmts;
+ const struct smiapp_csi_data_format *csi_format;
+ const struct smiapp_csi_data_format *internal_csi_format;
+ u32 default_mbus_frame_fmts;
+ int default_pixel_order;
+
+ u8 binning_horizontal;
+ u8 binning_vertical;
+
+ u8 scale_m;
+ u8 scaling_mode;
+
+ u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */
+ u8 flash_capability;
+ u8 frame_skip;
+
+ int power_count;
+
+ bool streaming;
+ bool dev_init_done;
+
+ u8 *nvm; /* nvm memory buffer */
+ unsigned int nvm_size; /* bytes */
+
+ struct smiapp_module_info minfo;
+
+ struct smiapp_pll pll;
+
+ /* Pixel array controls */
+ struct v4l2_ctrl *analog_gain;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *pixel_rate_parray;
+ /* src controls */
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate_csi;
+};
+
+#define to_smiapp_subdev(_sd) \
+ container_of(_sd, struct smiapp_subdev, sd)
+
+#define to_smiapp_sensor(_sd) \
+ (to_smiapp_subdev(_sd)->sensor)
+
+#endif /* __SMIAPP_PRIV_H_ */
diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c
index c2882fa5be8..19ea780b16f 100644
--- a/drivers/media/video/sn9c102/sn9c102_core.c
+++ b/drivers/media/video/sn9c102/sn9c102_core.c
@@ -995,10 +995,8 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam)
static int sn9c102_stream_interrupt(struct sn9c102_device* cam)
{
- long timeout;
-
cam->stream = STREAM_INTERRUPT;
- timeout = wait_event_timeout(cam->wait_stream,
+ wait_event_timeout(cam->wait_stream,
(cam->stream == STREAM_OFF) ||
(cam->state & DEV_DISCONNECTED),
SN9C102_URB_TIMEOUT);
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index aedb970d13f..0421bf9453b 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -164,35 +164,38 @@ static int soc_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
pixfmtstr(pix->pixelformat), pix->width, pix->height);
- pix->bytesperline = 0;
- pix->sizeimage = 0;
+ if (!(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) {
+ pix->bytesperline = 0;
+ pix->sizeimage = 0;
+ }
ret = ici->ops->try_fmt(icd, f);
if (ret < 0)
return ret;
- if (!pix->sizeimage) {
- if (!pix->bytesperline) {
- const struct soc_camera_format_xlate *xlate;
+ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
+ if (!xlate)
+ return -EINVAL;
+
+ ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ pix->bytesperline = max_t(u32, pix->bytesperline, ret);
- xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
- if (!xlate)
- return -EINVAL;
+ ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline,
+ pix->height);
+ if (ret < 0)
+ return ret;
- ret = soc_mbus_bytes_per_line(pix->width,
- xlate->host_fmt);
- if (ret > 0)
- pix->bytesperline = ret;
- }
- if (pix->bytesperline)
- pix->sizeimage = pix->bytesperline * pix->height;
- }
+ pix->sizeimage = max_t(u32, pix->sizeimage, ret);
return 0;
}
@@ -257,13 +260,13 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a)
return v4l2_subdev_call(sd, core, g_std, a);
}
-static int soc_camera_enum_fsizes(struct file *file, void *fh,
+static int soc_camera_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct soc_camera_device *icd = file->private_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- return ici->ops->enum_fsizes(icd, fsize);
+ return ici->ops->enum_framesizes(icd, fsize);
}
static int soc_camera_reqbufs(struct file *file, void *priv,
@@ -1244,8 +1247,8 @@ static int default_s_parm(struct soc_camera_device *icd,
return v4l2_subdev_call(sd, video, s_parm, parm);
}
-static int default_enum_fsizes(struct soc_camera_device *icd,
- struct v4l2_frmsizeenum *fsize)
+static int default_enum_framesizes(struct soc_camera_device *icd,
+ struct v4l2_frmsizeenum *fsize)
{
int ret;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
@@ -1259,7 +1262,7 @@ static int default_enum_fsizes(struct soc_camera_device *icd,
/* map xlate-code to pixel_format, sensor only handle xlate-code*/
fsize_mbus.pixel_format = xlate->code;
- ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, &fsize_mbus);
+ ret = v4l2_subdev_call(sd, video, enum_framesizes, &fsize_mbus);
if (ret < 0)
return ret;
@@ -1298,8 +1301,8 @@ int soc_camera_host_register(struct soc_camera_host *ici)
ici->ops->set_parm = default_s_parm;
if (!ici->ops->get_parm)
ici->ops->get_parm = default_g_parm;
- if (!ici->ops->enum_fsizes)
- ici->ops->enum_fsizes = default_enum_fsizes;
+ if (!ici->ops->enum_framesizes)
+ ici->ops->enum_framesizes = default_enum_framesizes;
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
@@ -1390,7 +1393,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_s_input = soc_camera_s_input,
.vidioc_s_std = soc_camera_s_std,
.vidioc_g_std = soc_camera_g_std,
- .vidioc_enum_framesizes = soc_camera_enum_fsizes,
+ .vidioc_enum_framesizes = soc_camera_enum_framesizes,
.vidioc_reqbufs = soc_camera_reqbufs,
.vidioc_querybuf = soc_camera_querybuf,
.vidioc_qbuf = soc_camera_qbuf,
@@ -1429,6 +1432,10 @@ static int video_dev_create(struct soc_camera_device *icd)
vdev->tvnorms = V4L2_STD_UNKNOWN;
vdev->ctrl_handler = &icd->ctrl_handler;
vdev->lock = &icd->video_lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
icd->vdev = vdev;
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
index cf7f2194ded..89dce097a82 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/video/soc_mediabus.c
@@ -24,6 +24,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YVYU8_2X8,
@@ -33,6 +34,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_UYVY8_2X8,
@@ -42,6 +44,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_VYUY8_2X8,
@@ -51,6 +54,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE,
@@ -60,6 +64,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
@@ -69,6 +74,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB565_2X8_LE,
@@ -78,6 +84,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB565_2X8_BE,
@@ -87,6 +94,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR8_1X8,
@@ -96,6 +104,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_1X10,
@@ -105,6 +114,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_Y8_1X8,
@@ -114,6 +124,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_Y10_1X10,
@@ -123,6 +134,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
@@ -132,6 +144,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
@@ -141,6 +154,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADLO,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
@@ -150,6 +164,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
@@ -159,6 +174,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADLO,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_JPEG_1X8,
@@ -168,6 +184,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_VARIABLE,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE,
@@ -177,6 +194,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_BE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YUYV8_1_5X8,
@@ -186,6 +204,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YVYU8_1_5X8,
@@ -195,6 +214,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_UYVY8_1X16,
@@ -204,6 +224,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_VYUY8_1X16,
@@ -213,6 +234,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YUYV8_1X16,
@@ -222,6 +244,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_YVYU8_1X16,
@@ -231,6 +254,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG8_1X8,
@@ -240,6 +264,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
@@ -249,6 +274,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGBRG10_1X10,
@@ -258,6 +284,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG10_1X10,
@@ -267,6 +294,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SRGGB10_1X10,
@@ -276,6 +304,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 10,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SBGGR12_1X12,
@@ -285,6 +314,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGBRG12_1X12,
@@ -294,6 +324,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SGRBG12_1X12,
@@ -303,6 +334,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
}, {
.code = V4L2_MBUS_FMT_SRGGB12_1X12,
@@ -312,6 +344,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
},
},
};
@@ -345,6 +378,9 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel);
s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
{
+ if (mf->layout != SOC_MBUS_LAYOUT_PACKED)
+ return width * mf->bits_per_sample / 8;
+
switch (mf->packing) {
case SOC_MBUS_PACKING_NONE:
return width * mf->bits_per_sample / 8;
@@ -361,6 +397,24 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
}
EXPORT_SYMBOL(soc_mbus_bytes_per_line);
+s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf,
+ u32 bytes_per_line, u32 height)
+{
+ if (mf->layout == SOC_MBUS_LAYOUT_PACKED)
+ return bytes_per_line * height;
+
+ switch (mf->packing) {
+ case SOC_MBUS_PACKING_2X8_PADHI:
+ case SOC_MBUS_PACKING_2X8_PADLO:
+ return bytes_per_line * height * 2;
+ case SOC_MBUS_PACKING_1_5X8:
+ return bytes_per_line * height * 3 / 2;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(soc_mbus_image_size);
+
const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc(
enum v4l2_mbus_pixelcode code,
const struct soc_mbus_lookup *lookup,
diff --git a/drivers/media/video/sta2x11_vip.c b/drivers/media/video/sta2x11_vip.c
new file mode 100644
index 00000000000..4c10205264d
--- /dev/null
+++ b/drivers/media/video/sta2x11_vip.c
@@ -0,0 +1,1550 @@
+/*
+ * This is the driver for the STA2x11 Video Input Port.
+ *
+ * Copyright (C) 2010 WindRiver Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Author: Andreas Kies <andreas.kies@windriver.com>
+ * Vlad Lungu <vlad.lungu@windriver.com>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+
+#include <linux/videodev2.h>
+
+#include <linux/kmod.h>
+
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf-dma-contig.h>
+
+#include "sta2x11_vip.h"
+
+#define DRV_NAME "sta2x11_vip"
+#define DRV_VERSION "1.3"
+
+#ifndef PCI_DEVICE_ID_STMICRO_VIP
+#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D
+#endif
+
+#define MAX_FRAMES 4
+
+/*Register offsets*/
+#define DVP_CTL 0x00
+#define DVP_TFO 0x04
+#define DVP_TFS 0x08
+#define DVP_BFO 0x0C
+#define DVP_BFS 0x10
+#define DVP_VTP 0x14
+#define DVP_VBP 0x18
+#define DVP_VMP 0x1C
+#define DVP_ITM 0x98
+#define DVP_ITS 0x9C
+#define DVP_STA 0xA0
+#define DVP_HLFLN 0xA8
+#define DVP_RGB 0xC0
+#define DVP_PKZ 0xF0
+
+/*Register fields*/
+#define DVP_CTL_ENA 0x00000001
+#define DVP_CTL_RST 0x80000000
+#define DVP_CTL_DIS (~0x00040001)
+
+#define DVP_IT_VSB 0x00000008
+#define DVP_IT_VST 0x00000010
+#define DVP_IT_FIFO 0x00000020
+
+#define DVP_HLFLN_SD 0x00000001
+
+#define REG_WRITE(vip, reg, value) iowrite32((value), (vip->iomem)+(reg))
+#define REG_READ(vip, reg) ioread32((vip->iomem)+(reg))
+
+#define SAVE_COUNT 8
+#define AUX_COUNT 3
+#define IRQ_COUNT 1
+
+/**
+ * struct sta2x11_vip - All internal data for one instance of device
+ * @v4l2_dev: device registered in v4l layer
+ * @video_dev: properties of our device
+ * @pdev: PCI device
+ * @adapter: contains I2C adapter information
+ * @register_save_area: All relevant register are saved here during suspend
+ * @decoder: contains information about video DAC
+ * @format: pixel format, fixed UYVY
+ * @std: video standard (e.g. PAL/NTSC)
+ * @input: input line for video signal ( 0 or 1 )
+ * @users: Number of open of device ( max. 1 )
+ * @disabled: Device is in power down state
+ * @mutex: ensures exclusive opening of device
+ * @slock: for excluse acces of registers
+ * @vb_vidq: queue maintained by videobuf layer
+ * @capture: linked list of capture buffer
+ * @active: struct videobuf_buffer currently beingg filled
+ * @started: device is ready to capture frame
+ * @closing: device will be shut down
+ * @tcount: Number of top frames
+ * @bcount: Number of bottom frames
+ * @overflow: Number of FIFO overflows
+ * @mem_spare: small buffer of unused frame
+ * @dma_spare: dma addres of mem_spare
+ * @iomem: hardware base address
+ * @config: I2C and gpio config from platform
+ *
+ * All non-local data is accessed via this structure.
+ */
+
+struct sta2x11_vip {
+ struct v4l2_device v4l2_dev;
+ struct video_device *video_dev;
+ struct pci_dev *pdev;
+ struct i2c_adapter *adapter;
+ unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT];
+ struct v4l2_subdev *decoder;
+ struct v4l2_pix_format format;
+ v4l2_std_id std;
+ unsigned int input;
+ int users;
+ int disabled;
+ struct mutex mutex; /* exclusive access during open */
+ spinlock_t slock; /* spin lock for hardware and queue access */
+ struct videobuf_queue vb_vidq;
+ struct list_head capture;
+ struct videobuf_buffer *active;
+ int started, closing, tcount, bcount;
+ int overflow;
+ void *mem_spare;
+ dma_addr_t dma_spare;
+ void *iomem;
+ struct vip_config *config;
+};
+
+static const unsigned int registers_to_save[AUX_COUNT] = {
+ DVP_HLFLN, DVP_RGB, DVP_PKZ
+};
+
+static struct v4l2_pix_format formats_50[] = {
+ { /*PAL interlaced */
+ .width = 720,
+ .height = 576,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 2 * 576,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M},
+ { /*PAL top */
+ .width = 720,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_TOP,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 2 * 288,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M},
+ { /*PAL bottom */
+ .width = 720,
+ .height = 288,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_BOTTOM,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 2 * 288,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M},
+
+};
+
+static struct v4l2_pix_format formats_60[] = {
+ { /*NTSC interlaced */
+ .width = 720,
+ .height = 480,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 2 * 480,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M},
+ { /*NTSC top */
+ .width = 720,
+ .height = 240,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_TOP,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 2 * 240,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M},
+ { /*NTSC bottom */
+ .width = 720,
+ .height = 240,
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .field = V4L2_FIELD_BOTTOM,
+ .bytesperline = 720 * 2,
+ .sizeimage = 720 * 2 * 240,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M},
+};
+
+/**
+ * buf_setup - Get size and number of video buffer
+ * @vq: queue in videobuf
+ * @count: Number of buffers (1..MAX_FRAMES).
+ * 0 use default value.
+ * @size: size of buffer in bytes
+ *
+ * returns size and number of buffers
+ * a preset value of 0 returns the default number.
+ * return value: 0, always succesfull.
+ */
+static int buf_setup(struct videobuf_queue *vq, unsigned int *count,
+ unsigned int *size)
+{
+ struct sta2x11_vip *vip = vq->priv_data;
+
+ *size = vip->format.width * vip->format.height * 2;
+ if (0 == *count || MAX_FRAMES < *count)
+ *count = MAX_FRAMES;
+ return 0;
+};
+
+/**
+ * buf_prepare - prepare buffer for usage
+ * @vq: queue in videobuf layer
+ * @vb: buffer to be prepared
+ * @field: type of video data (interlaced/non-interlaced)
+ *
+ * Allocate or realloc buffer
+ * return value: 0, successful.
+ *
+ * -EINVAL, supplied buffer is too small.
+ *
+ * other, buffer could not be locked.
+ */
+static int buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct sta2x11_vip *vip = vq->priv_data;
+ int ret;
+
+ vb->size = vip->format.width * vip->format.height * 2;
+ if ((0 != vb->baddr) && (vb->bsize < vb->size))
+ return -EINVAL;
+ vb->width = vip->format.width;
+ vb->height = vip->format.height;
+ vb->field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == vb->state) {
+ ret = videobuf_iolock(vq, vb, NULL);
+ if (ret)
+ goto fail;
+ }
+ vb->state = VIDEOBUF_PREPARED;
+ return 0;
+fail:
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+ return ret;
+}
+
+/**
+ * buf_queu - queue buffer for filling
+ * @vq: queue in videobuf layer
+ * @vb: buffer to be queued
+ *
+ * if capturing is already running, the buffer will be queued. Otherwise
+ * capture is started and the buffer is used directly.
+ */
+static void buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct sta2x11_vip *vip = vq->priv_data;
+ u32 dma;
+
+ vb->state = VIDEOBUF_QUEUED;
+
+ if (vip->active) {
+ list_add_tail(&vb->queue, &vip->capture);
+ return;
+ }
+
+ vip->started = 1;
+ vip->tcount = 0;
+ vip->bcount = 0;
+ vip->active = vb;
+ vb->state = VIDEOBUF_ACTIVE;
+
+ dma = videobuf_to_dma_contig(vb);
+
+ REG_WRITE(vip, DVP_TFO, (0 << 16) | (0));
+ /* despite of interlace mode, upper and lower frames start at zero */
+ REG_WRITE(vip, DVP_BFO, (0 << 16) | (0));
+
+ switch (vip->format.field) {
+ case V4L2_FIELD_INTERLACED:
+ REG_WRITE(vip, DVP_TFS,
+ ((vip->format.height / 2 - 1) << 16) |
+ (2 * vip->format.width - 1));
+ REG_WRITE(vip, DVP_BFS, ((vip->format.height / 2 - 1) << 16) |
+ (2 * vip->format.width - 1));
+ REG_WRITE(vip, DVP_VTP, dma);
+ REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2);
+ REG_WRITE(vip, DVP_VMP, 4 * vip->format.width);
+ break;
+ case V4L2_FIELD_TOP:
+ REG_WRITE(vip, DVP_TFS,
+ ((vip->format.height - 1) << 16) |
+ (2 * vip->format.width - 1));
+ REG_WRITE(vip, DVP_BFS, ((0) << 16) |
+ (2 * vip->format.width - 1));
+ REG_WRITE(vip, DVP_VTP, dma);
+ REG_WRITE(vip, DVP_VBP, dma);
+ REG_WRITE(vip, DVP_VMP, 2 * vip->format.width);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ REG_WRITE(vip, DVP_TFS, ((0) << 16) |
+ (2 * vip->format.width - 1));
+ REG_WRITE(vip, DVP_BFS,
+ ((vip->format.height) << 16) |
+ (2 * vip->format.width - 1));
+ REG_WRITE(vip, DVP_VTP, dma);
+ REG_WRITE(vip, DVP_VBP, dma);
+ REG_WRITE(vip, DVP_VMP, 2 * vip->format.width);
+ break;
+
+ default:
+ pr_warning("VIP: unknown field format\n");
+ return;
+ }
+
+ REG_WRITE(vip, DVP_CTL, DVP_CTL_ENA);
+}
+
+/**
+ * buff_release - release buffer
+ * @vq: queue in videobuf layer
+ * @vb: buffer to be released
+ *
+ * release buffer in videobuf layer
+ */
+static void buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+
+ videobuf_dma_contig_free(vq, vb);
+ vb->state = VIDEOBUF_NEEDS_INIT;
+}
+
+static struct videobuf_queue_ops vip_qops = {
+ .buf_setup = buf_setup,
+ .buf_prepare = buf_prepare,
+ .buf_queue = buf_queue,
+ .buf_release = buf_release,
+};
+
+/**
+ * vip_open - open video device
+ * @file: descriptor of device
+ *
+ * open device, make sure it is only opened once.
+ * return value: 0, no error.
+ *
+ * -EBUSY, device is already opened
+ *
+ * -ENOMEM, no memory for auxiliary DMA buffer
+ */
+static int vip_open(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ mutex_lock(&vip->mutex);
+ vip->users++;
+
+ if (vip->users > 1) {
+ vip->users--;
+ mutex_unlock(&vip->mutex);
+ return -EBUSY;
+ }
+
+ file->private_data = dev;
+ vip->overflow = 0;
+ vip->started = 0;
+ vip->closing = 0;
+ vip->active = NULL;
+
+ INIT_LIST_HEAD(&vip->capture);
+ vip->mem_spare = dma_alloc_coherent(&vip->pdev->dev, 64,
+ &vip->dma_spare, GFP_KERNEL);
+ if (!vip->mem_spare) {
+ vip->users--;
+ mutex_unlock(&vip->mutex);
+ return -ENOMEM;
+ }
+
+ mutex_unlock(&vip->mutex);
+ videobuf_queue_dma_contig_init_cached(&vip->vb_vidq,
+ &vip_qops,
+ &vip->pdev->dev,
+ &vip->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct videobuf_buffer),
+ vip, NULL);
+ REG_READ(vip, DVP_ITS);
+ REG_WRITE(vip, DVP_HLFLN, DVP_HLFLN_SD);
+ REG_WRITE(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST);
+ REG_WRITE(vip, DVP_CTL, DVP_CTL_RST);
+ REG_WRITE(vip, DVP_CTL, 0);
+ REG_READ(vip, DVP_ITS);
+ return 0;
+}
+
+/**
+ * vip_close - close video device
+ * @file: descriptor of device
+ *
+ * close video device, wait until all pending operations are finished
+ * ( maximum FRAME_MAX buffers pending )
+ * Turn off interrupts.
+ *
+ * return value: 0, always succesful.
+ */
+static int vip_close(struct file *file)
+{
+ struct video_device *dev = video_devdata(file);
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ vip->closing = 1;
+ if (vip->active)
+ videobuf_waiton(&vip->vb_vidq, vip->active, 0, 0);
+ spin_lock_irq(&vip->slock);
+
+ REG_WRITE(vip, DVP_ITM, 0);
+ REG_WRITE(vip, DVP_CTL, DVP_CTL_RST);
+ REG_WRITE(vip, DVP_CTL, 0);
+ REG_READ(vip, DVP_ITS);
+
+ vip->started = 0;
+ vip->active = NULL;
+
+ spin_unlock_irq(&vip->slock);
+
+ videobuf_stop(&vip->vb_vidq);
+ videobuf_mmap_free(&vip->vb_vidq);
+
+ dma_free_coherent(&vip->pdev->dev, 64, vip->mem_spare, vip->dma_spare);
+ file->private_data = NULL;
+ mutex_lock(&vip->mutex);
+ vip->users--;
+ mutex_unlock(&vip->mutex);
+ return 0;
+}
+
+/**
+ * vip_read - read from video input
+ * @file: descriptor of device
+ * @data: user buffer
+ * @count: number of bytes to be read
+ * @ppos: position within stream
+ *
+ * read video data from video device.
+ * handling is done in generic videobuf layer
+ * return value: provided by videobuf layer
+ */
+static ssize_t vip_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct video_device *dev = file->private_data;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_read_stream(&vip->vb_vidq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+/**
+ * vip_mmap - map user buffer
+ * @file: descriptor of device
+ * @vma: user buffer
+ *
+ * map user space buffer into kernel mode, including DMA address.
+ * handling is done in generic videobuf layer.
+ * return value: provided by videobuf layer
+ */
+static int vip_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct video_device *dev = file->private_data;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_mmap_mapper(&vip->vb_vidq, vma);
+}
+
+/**
+ * vip_poll - poll for event
+ * @file: descriptor of device
+ * @wait: contains events to be waited for
+ *
+ * wait for event related to video device.
+ * handling is done in generic videobuf layer.
+ * return value: provided by videobuf layer
+ */
+static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct video_device *dev = file->private_data;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_poll_stream(file, &vip->vb_vidq, wait);
+}
+
+/**
+ * vidioc_querycap - return capabilities of device
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @cap: contains return values
+ *
+ * the capabilities of the device are returned
+ *
+ * return value: 0, no error.
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ memset(cap, 0, sizeof(struct v4l2_capability));
+ strcpy(cap->driver, DRV_NAME);
+ strcpy(cap->card, DRV_NAME);
+ cap->version = 0;
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+ pci_name(vip->pdev));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+/**
+ * vidioc_s_std - set video standard
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @std: contains standard to be set
+ *
+ * the video standard is set
+ *
+ * return value: 0, no error.
+ *
+ * -EIO, no input signal detected
+ *
+ * other, returned from video DAC.
+ */
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+ v4l2_std_id oldstd = vip->std, newstd;
+ int status;
+
+ if (V4L2_STD_ALL == *std) {
+ v4l2_subdev_call(vip->decoder, core, s_std, *std);
+ ssleep(2);
+ v4l2_subdev_call(vip->decoder, video, querystd, &newstd);
+ v4l2_subdev_call(vip->decoder, video, g_input_status, &status);
+ if (status & V4L2_IN_ST_NO_SIGNAL)
+ return -EIO;
+ *std = vip->std = newstd;
+ if (oldstd != *std) {
+ if (V4L2_STD_525_60 & (*std))
+ vip->format = formats_60[0];
+ else
+ vip->format = formats_50[0];
+ }
+ return 0;
+ }
+
+ if (oldstd != *std) {
+ if (V4L2_STD_525_60 & (*std))
+ vip->format = formats_60[0];
+ else
+ vip->format = formats_50[0];
+ }
+
+ return v4l2_subdev_call(vip->decoder, core, s_std, *std);
+}
+
+/**
+ * vidioc_g_std - get video standard
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @std: contains return values
+ *
+ * the current video standard is returned
+ *
+ * return value: 0, no error.
+ */
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ *std = vip->std;
+ return 0;
+}
+
+/**
+ * vidioc_querystd - get possible video standards
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @std: contains return values
+ *
+ * all possible video standards are returned
+ *
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return v4l2_subdev_call(vip->decoder, video, querystd, std);
+
+}
+
+/**
+ * vidioc_queryctl - get possible control settings
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @ctrl: contains return values
+ *
+ * return possible values for a control
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *ctrl)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return v4l2_subdev_call(vip->decoder, core, queryctrl, ctrl);
+}
+
+/**
+ * vidioc_g_ctl - get control value
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @ctrl: contains return values
+ *
+ * return setting for a control value
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return v4l2_subdev_call(vip->decoder, core, g_ctrl, ctrl);
+}
+
+/**
+ * vidioc_s_ctl - set control value
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @ctrl: contains value to be set
+ *
+ * set value for a specific control
+ * return value: delivered by video DAC routine.
+ */
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return v4l2_subdev_call(vip->decoder, core, s_ctrl, ctrl);
+}
+
+/**
+ * vidioc_enum_input - return name of input line
+ * @file: descriptor of device (not used)
+ * @priv: points to current videodevice
+ * @inp: contains return values
+ *
+ * the user friendly name of the input line is returned
+ *
+ * return value: 0, no error.
+ *
+ * -EINVAL, input line number out of range
+ */
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+{
+ if (inp->index > 1)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_ALL;
+ sprintf(inp->name, "Camera %u", inp->index);
+
+ return 0;
+}
+
+/**
+ * vidioc_s_input - set input line
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @i: new input line number
+ *
+ * the current active input line is set
+ *
+ * return value: 0, no error.
+ *
+ * -EINVAL, line number out of range
+ */
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+ int ret;
+
+ if (i > 1)
+ return -EINVAL;
+ ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0);
+
+ if (!ret)
+ vip->input = i;
+
+ return 0;
+}
+
+/**
+ * vidioc_g_input - return input line
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @i: returned input line number
+ *
+ * the current active input line is returned
+ *
+ * return value: always 0.
+ */
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ *i = vip->input;
+ return 0;
+}
+
+/**
+ * vidioc_enum_fmt_vid_cap - return video capture format
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: returned format information
+ *
+ * returns name and format of video capture
+ * Only UYVY is supported by hardware.
+ *
+ * return value: always 0.
+ */
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+
+ if (f->index != 0)
+ return -EINVAL;
+
+ strcpy(f->description, "4:2:2, packed, UYVY");
+ f->pixelformat = V4L2_PIX_FMT_UYVY;
+ f->flags = 0;
+ return 0;
+}
+
+/**
+ * vidioc_try_fmt_vid_cap - set video capture format
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: new format
+ *
+ * new video format is set which includes width and
+ * field type. width is fixed to 720, no scaling.
+ * Only UYVY is supported by this hardware.
+ * the minimum height is 200, the maximum is 576 (PAL)
+ *
+ * return value: 0, no error
+ *
+ * -EINVAL, pixel or field format not supported
+ *
+ */
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+ int interlace_lim;
+
+ if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat)
+ return -EINVAL;
+
+ if (V4L2_STD_525_60 & vip->std)
+ interlace_lim = 240;
+ else
+ interlace_lim = 288;
+
+ switch (f->fmt.pix.field) {
+ case V4L2_FIELD_ANY:
+ if (interlace_lim < f->fmt.pix.height)
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ else
+ f->fmt.pix.field = V4L2_FIELD_BOTTOM;
+ break;
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ if (interlace_lim < f->fmt.pix.height)
+ f->fmt.pix.height = interlace_lim;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.height &= ~1;
+ if (2 * interlace_lim < f->fmt.pix.height)
+ f->fmt.pix.height = 2 * interlace_lim;
+ if (200 > f->fmt.pix.height)
+ f->fmt.pix.height = 200;
+ f->fmt.pix.width = 720;
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.priv = 0;
+ return 0;
+}
+
+/**
+ * vidioc_s_fmt_vid_cap - set current video format parameters
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: returned format information
+ *
+ * set new capture format
+ * return value: 0, no error
+ *
+ * other, delivered by video DAC routine.
+ */
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ memcpy(&vip->format, &f->fmt.pix, sizeof(struct v4l2_pix_format));
+ return 0;
+}
+
+/**
+ * vidioc_g_fmt_vid_cap - get current video format parameters
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @f: contains format information
+ *
+ * returns current video format parameters
+ *
+ * return value: 0, always successful
+ */
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ memcpy(&f->fmt.pix, &vip->format, sizeof(struct v4l2_pix_format));
+ return 0;
+}
+
+/**
+ * vidioc_reqfs - request buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_reqbufs(&vip->vb_vidq, p);
+}
+
+/**
+ * vidioc_querybuf - query buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * query buffer state.
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_querybuf(&vip->vb_vidq, p);
+}
+
+/**
+ * vidioc_qbuf - queue a buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_qbuf(&vip->vb_vidq, p);
+}
+
+/**
+ * vidioc_dqbuf - dequeue a buffer
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @p: video buffer
+ *
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_dqbuf(&vip->vb_vidq, p, file->f_flags & O_NONBLOCK);
+}
+
+/**
+ * vidioc_streamon - turn on streaming
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @type: type of capture
+ *
+ * turn on streaming.
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_streamon(&vip->vb_vidq);
+}
+
+/**
+ * vidioc_streamoff - turn off streaming
+ * @file: descriptor of device ( not used)
+ * @priv: points to current videodevice
+ * @type: type of capture
+ *
+ * turn off streaming.
+ * Handling is done in generic videobuf layer.
+ */
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct video_device *dev = priv;
+ struct sta2x11_vip *vip = video_get_drvdata(dev);
+
+ return videobuf_streamoff(&vip->vb_vidq);
+}
+
+static const struct v4l2_file_operations vip_fops = {
+ .owner = THIS_MODULE,
+ .open = vip_open,
+ .release = vip_close,
+ .ioctl = video_ioctl2,
+ .read = vip_read,
+ .mmap = vip_mmap,
+ .poll = vip_poll
+};
+
+static const struct v4l2_ioctl_ops vip_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_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,
+};
+
+static struct video_device video_dev_template = {
+ .name = DRV_NAME,
+ .release = video_device_release,
+ .fops = &vip_fops,
+ .ioctl_ops = &vip_ioctl_ops,
+ .tvnorms = V4L2_STD_ALL,
+};
+
+/**
+ * vip_irq - interrupt routine
+ * @irq: Number of interrupt ( not used, correct number is assumed )
+ * @vip: local data structure containing all information
+ *
+ * check for both frame interrupts set ( top and bottom ).
+ * check FIFO overflow, but limit number of log messages after open.
+ * signal a complete buffer if done.
+ * dequeue a new buffer if available.
+ * disable VIP if no buffer available.
+ *
+ * return value: IRQ_NONE, interrupt was not generated by VIP
+ *
+ * IRQ_HANDLED, interrupt done.
+ */
+static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip)
+{
+ u32 status, dma;
+ unsigned long flags;
+ struct videobuf_buffer *vb;
+
+ status = REG_READ(vip, DVP_ITS);
+
+ if (!status) {
+ pr_debug("VIP: irq ignored\n");
+ return IRQ_NONE;
+ }
+
+ if (!vip->started)
+ return IRQ_HANDLED;
+
+ if (status & DVP_IT_VSB)
+ vip->bcount++;
+
+ if (status & DVP_IT_VST)
+ vip->tcount++;
+
+ if ((DVP_IT_VSB | DVP_IT_VST) == (status & (DVP_IT_VST | DVP_IT_VSB))) {
+ /* this is bad, we are too slow, hope the condition is gone
+ * on the next frame */
+ pr_info("VIP: both irqs\n");
+ return IRQ_HANDLED;
+ }
+
+ if (status & DVP_IT_FIFO) {
+ if (5 > vip->overflow++)
+ pr_info("VIP: fifo overflow\n");
+ }
+
+ if (2 > vip->tcount)
+ return IRQ_HANDLED;
+
+ if (status & DVP_IT_VSB)
+ return IRQ_HANDLED;
+
+ spin_lock_irqsave(&vip->slock, flags);
+
+ REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA);
+ if (vip->active) {
+ do_gettimeofday(&vip->active->ts);
+ vip->active->field_count++;
+ vip->active->state = VIDEOBUF_DONE;
+ wake_up(&vip->active->done);
+ vip->active = NULL;
+ }
+ if (!vip->closing) {
+ if (list_empty(&vip->capture))
+ goto done;
+
+ vb = list_first_entry(&vip->capture, struct videobuf_buffer,
+ queue);
+ if (NULL == vb) {
+ pr_info("VIP: no buffer\n");
+ goto done;
+ }
+ vb->state = VIDEOBUF_ACTIVE;
+ list_del(&vb->queue);
+ vip->active = vb;
+ dma = videobuf_to_dma_contig(vb);
+ switch (vip->format.field) {
+ case V4L2_FIELD_INTERLACED:
+ REG_WRITE(vip, DVP_VTP, dma);
+ REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2);
+ break;
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ REG_WRITE(vip, DVP_VTP, dma);
+ REG_WRITE(vip, DVP_VBP, dma);
+ break;
+ default:
+ pr_warning("VIP: unknown field format\n");
+ goto done;
+ break;
+ }
+ REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) | DVP_CTL_ENA);
+ }
+done:
+ spin_unlock_irqrestore(&vip->slock, flags);
+ return IRQ_HANDLED;
+}
+
+/**
+ * vip_gpio_reserve - reserve gpio pin
+ * @dev: device
+ * @pin: GPIO pin number
+ * @dir: direction, input or output
+ * @name: GPIO pin name
+ *
+ */
+static int vip_gpio_reserve(struct device *dev, int pin, int dir,
+ const char *name)
+{
+ int ret;
+
+ if (pin == -1)
+ return 0;
+
+ ret = gpio_request(pin, name);
+ if (ret) {
+ dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name);
+ return ret;
+ }
+
+ ret = gpio_direction_output(pin, dir);
+ if (ret) {
+ dev_err(dev, "Failed to set direction for pin %d (%s)\n",
+ pin, name);
+ gpio_free(pin);
+ return ret;
+ }
+
+ ret = gpio_export(pin, false);
+ if (ret) {
+ dev_err(dev, "Failed to export pin %d (%s)\n", pin, name);
+ gpio_free(pin);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * vip_gpio_release - release gpio pin
+ * @dev: device
+ * @pin: GPIO pin number
+ * @name: GPIO pin name
+ *
+ */
+static void vip_gpio_release(struct device *dev, int pin, const char *name)
+{
+ if (pin != -1) {
+ dev_dbg(dev, "releasing pin %d (%s)\n", pin, name);
+ gpio_unexport(pin);
+ gpio_free(pin);
+ }
+}
+
+/**
+ * sta2x11_vip_init_one - init one instance of video device
+ * @pdev: PCI device
+ * @ent: (not used)
+ *
+ * allocate reset pins for DAC.
+ * Reset video DAC, this is done via reset line.
+ * allocate memory for managing device
+ * request interrupt
+ * map IO region
+ * register device
+ * find and initialize video DAC
+ *
+ * return value: 0, no error
+ *
+ * -ENOMEM, no memory
+ *
+ * -ENODEV, device could not be detected or registered
+ */
+static int __devinit sta2x11_vip_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int ret;
+ struct sta2x11_vip *vip;
+ struct vip_config *config;
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ config = dev_get_platdata(&pdev->dev);
+ if (!config) {
+ dev_info(&pdev->dev, "VIP slot disabled\n");
+ ret = -EINVAL;
+ goto disable;
+ }
+
+ ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0,
+ config->pwr_name);
+ if (ret)
+ goto disable;
+
+ if (config->reset_pin >= 0) {
+ ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0,
+ config->reset_name);
+ if (ret) {
+ vip_gpio_release(&pdev->dev, config->pwr_pin,
+ config->pwr_name);
+ goto disable;
+ }
+ }
+
+ if (config->pwr_pin != -1) {
+ /* Datasheet says 5ms between PWR and RST */
+ usleep_range(5000, 25000);
+ ret = gpio_direction_output(config->pwr_pin, 1);
+ }
+
+ if (config->reset_pin != -1) {
+ /* Datasheet says 5ms between PWR and RST */
+ usleep_range(5000, 25000);
+ ret = gpio_direction_output(config->reset_pin, 1);
+ }
+ usleep_range(5000, 25000);
+
+ vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL);
+ if (!vip) {
+ ret = -ENOMEM;
+ goto release_gpios;
+ }
+
+ vip->pdev = pdev;
+ vip->std = V4L2_STD_PAL;
+ vip->format = formats_50[0];
+ vip->config = config;
+
+ if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev))
+ goto free_mem;
+
+ dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n",
+ (unsigned long)pci_resource_start(pdev, 0),
+ (unsigned long)pci_resource_len(pdev, 0), pdev->irq);
+
+ pci_set_master(pdev);
+
+ ret = pci_request_regions(pdev, DRV_NAME);
+ if (ret)
+ goto unreg;
+
+ vip->iomem = pci_iomap(pdev, 0, 0x100);
+ if (!vip->iomem) {
+ ret = -ENOMEM; /* FIXME */
+ goto release;
+ }
+
+ pci_enable_msi(pdev);
+
+ INIT_LIST_HEAD(&vip->capture);
+ spin_lock_init(&vip->slock);
+ mutex_init(&vip->mutex);
+ vip->started = 0;
+ vip->disabled = 0;
+
+ ret = request_irq(pdev->irq,
+ (irq_handler_t) vip_irq,
+ IRQF_SHARED, DRV_NAME, vip);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq failed\n");
+ ret = -ENODEV;
+ goto unmap;
+ }
+
+ vip->video_dev = video_device_alloc();
+ if (!vip->video_dev) {
+ ret = -ENOMEM;
+ goto release_irq;
+ }
+
+ *(vip->video_dev) = video_dev_template;
+ video_set_drvdata(vip->video_dev, vip);
+
+ ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto vrelease;
+
+ vip->adapter = i2c_get_adapter(vip->config->i2c_id);
+ if (!vip->adapter) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "no I2C adapter found\n");
+ goto vunreg;
+ }
+
+ vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter,
+ "adv7180", vip->config->i2c_addr,
+ NULL);
+ if (!vip->decoder) {
+ ret = -ENODEV;
+ dev_err(&pdev->dev, "no decoder found\n");
+ goto vunreg;
+ }
+
+ i2c_put_adapter(vip->adapter);
+
+ v4l2_subdev_call(vip->decoder, core, init, 0);
+
+ pr_info("STA2X11 Video Input Port (VIP) loaded\n");
+ return 0;
+
+vunreg:
+ video_set_drvdata(vip->video_dev, NULL);
+vrelease:
+ if (video_is_registered(vip->video_dev))
+ video_unregister_device(vip->video_dev);
+ else
+ video_device_release(vip->video_dev);
+release_irq:
+ free_irq(pdev->irq, vip);
+ pci_disable_msi(pdev);
+unmap:
+ pci_iounmap(pdev, vip->iomem);
+ mutex_destroy(&vip->mutex);
+release:
+ pci_release_regions(pdev);
+unreg:
+ v4l2_device_unregister(&vip->v4l2_dev);
+free_mem:
+ kfree(vip);
+release_gpios:
+ vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name);
+ vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name);
+disable:
+ /*
+ * do not call pci_disable_device on sta2x11 because it break all
+ * other Bus masters on this EP
+ */
+ return ret;
+}
+
+/**
+ * sta2x11_vip_remove_one - release device
+ * @pdev: PCI device
+ *
+ * Undo everything done in .._init_one
+ *
+ * unregister video device
+ * free interrupt
+ * unmap ioadresses
+ * free memory
+ * free GPIO pins
+ */
+static void __devexit sta2x11_vip_remove_one(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct sta2x11_vip *vip =
+ container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
+
+ video_set_drvdata(vip->video_dev, NULL);
+ video_unregister_device(vip->video_dev);
+ /*do not call video_device_release() here, is already done */
+ free_irq(pdev->irq, vip);
+ pci_disable_msi(pdev);
+ pci_iounmap(pdev, vip->iomem);
+ pci_release_regions(pdev);
+
+ v4l2_device_unregister(&vip->v4l2_dev);
+ mutex_destroy(&vip->mutex);
+
+ vip_gpio_release(&pdev->dev, vip->config->pwr_pin,
+ vip->config->pwr_name);
+ vip_gpio_release(&pdev->dev, vip->config->reset_pin,
+ vip->config->reset_name);
+
+ kfree(vip);
+ /*
+ * do not call pci_disable_device on sta2x11 because it break all
+ * other Bus masters on this EP
+ */
+}
+
+#ifdef CONFIG_PM
+
+/**
+ * sta2x11_vip_suspend - set device into power save mode
+ * @pdev: PCI device
+ * @state: new state of device
+ *
+ * all relevant registers are saved and an attempt to set a new state is made.
+ *
+ * return value: 0 always indicate success,
+ * even if device could not be disabled. (workaround for hardware problem)
+ *
+ * reurn value : 0, always succesful, even if hardware does not not support
+ * power down mode.
+ */
+static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct sta2x11_vip *vip =
+ container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&vip->slock, flags);
+ vip->register_save_area[0] = REG_READ(vip, DVP_CTL);
+ REG_WRITE(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS);
+ vip->register_save_area[SAVE_COUNT] = REG_READ(vip, DVP_ITM);
+ REG_WRITE(vip, DVP_ITM, 0);
+ for (i = 1; i < SAVE_COUNT; i++)
+ vip->register_save_area[i] = REG_READ(vip, 4 * i);
+ for (i = 0; i < AUX_COUNT; i++)
+ vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] =
+ REG_READ(vip, registers_to_save[i]);
+ spin_unlock_irqrestore(&vip->slock, flags);
+ /* save pci state */
+ pci_save_state(pdev);
+ if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) {
+ /*
+ * do not call pci_disable_device on sta2x11 because it
+ * break all other Bus masters on this EP
+ */
+ vip->disabled = 1;
+ }
+
+ pr_info("VIP: suspend\n");
+ return 0;
+}
+
+/**
+ * sta2x11_vip_resume - resume device operation
+ * @pdev : PCI device
+ *
+ * re-enable device, set PCI state to powered and restore registers.
+ * resume normal device operation afterwards.
+ *
+ * return value: 0, no error.
+ *
+ * other, could not set device to power on state.
+ */
+static int sta2x11_vip_resume(struct pci_dev *pdev)
+{
+ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
+ struct sta2x11_vip *vip =
+ container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev);
+ unsigned long flags;
+ int ret, i;
+
+ pr_info("VIP: resume\n");
+ /* restore pci state */
+ if (vip->disabled) {
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ pr_warning("VIP: Can't enable device.\n");
+ return ret;
+ }
+ vip->disabled = 0;
+ }
+ ret = pci_set_power_state(pdev, PCI_D0);
+ if (ret) {
+ /*
+ * do not call pci_disable_device on sta2x11 because it
+ * break all other Bus masters on this EP
+ */
+ pr_warning("VIP: Can't enable device.\n");
+ vip->disabled = 1;
+ return ret;
+ }
+
+ pci_restore_state(pdev);
+
+ spin_lock_irqsave(&vip->slock, flags);
+ for (i = 1; i < SAVE_COUNT; i++)
+ REG_WRITE(vip, 4 * i, vip->register_save_area[i]);
+ for (i = 0; i < AUX_COUNT; i++)
+ REG_WRITE(vip, registers_to_save[i],
+ vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]);
+ REG_WRITE(vip, DVP_CTL, vip->register_save_area[0]);
+ REG_WRITE(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]);
+ spin_unlock_irqrestore(&vip->slock, flags);
+ return 0;
+}
+
+#endif
+
+static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = {
+ {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)},
+ {0,}
+};
+
+static struct pci_driver sta2x11_vip_driver = {
+ .name = DRV_NAME,
+ .probe = sta2x11_vip_init_one,
+ .remove = __devexit_p(sta2x11_vip_remove_one),
+ .id_table = sta2x11_vip_pci_tbl,
+#ifdef CONFIG_PM
+ .suspend = sta2x11_vip_suspend,
+ .resume = sta2x11_vip_resume,
+#endif
+};
+
+static int __init sta2x11_vip_init_module(void)
+{
+ return pci_register_driver(&sta2x11_vip_driver);
+}
+
+static void __exit sta2x11_vip_exit_module(void)
+{
+ pci_unregister_driver(&sta2x11_vip_driver);
+}
+
+#ifdef MODULE
+module_init(sta2x11_vip_init_module);
+module_exit(sta2x11_vip_exit_module);
+#else
+late_initcall_sync(sta2x11_vip_init_module);
+#endif
+
+MODULE_DESCRIPTION("STA2X11 Video Input Port driver");
+MODULE_AUTHOR("Wind River");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("sta2x11 video input");
+MODULE_VERSION(DRV_VERSION);
+MODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl);
diff --git a/drivers/media/video/sta2x11_vip.h b/drivers/media/video/sta2x11_vip.h
new file mode 100644
index 00000000000..4f81a13666e
--- /dev/null
+++ b/drivers/media/video/sta2x11_vip.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2011 Wind River Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Anders Wallin <anders.wallin@windriver.com>
+ *
+ */
+
+#ifndef __STA2X11_VIP_H
+#define __STA2X11_VIP_H
+
+/**
+ * struct vip_config - video input configuration data
+ * @pwr_name: ADV powerdown name
+ * @pwr_pin: ADV powerdown pin
+ * @reset_name: ADV reset name
+ * @reset_pin: ADV reset pin
+ */
+struct vip_config {
+ const char *pwr_name;
+ int pwr_pin;
+ const char *reset_name;
+ int reset_pin;
+ int i2c_id;
+ int i2c_addr;
+};
+
+#endif /* __STA2X11_VIP_H */
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index d427f8436c7..86a0fc56c33 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -38,13 +38,13 @@
#include "stk-webcam.h"
-static bool hflip = 1;
+static bool hflip;
module_param(hflip, bool, 0444);
-MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 1");
+MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0");
-static bool vflip = 1;
+static bool vflip;
module_param(vflip, bool, 0444);
-MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 1");
+MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0");
static int debug;
module_param(debug, int, 0444);
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
index 465d7086bab..3d7ddd93282 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/video/tda9840.c
@@ -66,29 +66,53 @@ static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
val, reg);
}
+static int tda9840_status(struct v4l2_subdev *sd)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte;
+
+ 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;
+ }
+
+ v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte);
+ return byte & 0x60;
+}
+
static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
{
+ int stat = tda9840_status(sd);
int byte;
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:
+ stat = stat < 0 ? 0 : stat;
+ if (stat == 0 || stat == 0x60) /* mono input */
byte = TDA9840_SET_MONO;
- break;
+ else if (stat == 0x40) /* stereo input */
+ byte = (t->audmode == V4L2_TUNER_MODE_MONO) ?
+ TDA9840_SET_MONO : TDA9840_SET_STEREO;
+ else { /* bilingual */
+ switch (t->audmode) {
+ case V4L2_TUNER_MODE_LANG1_LANG2:
+ byte = TDA9840_SET_BOTH;
+ break;
+ case V4L2_TUNER_MODE_LANG2:
+ byte = TDA9840_SET_LANG2;
+ break;
+ default:
+ byte = TDA9840_SET_LANG1;
+ break;
+ }
}
v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte);
tda9840_write(sd, SWITCH, byte);
@@ -97,25 +121,14 @@ static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
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;
- }
+ int stat = tda9840_status(sd);
- if (byte & 0x80) {
- v4l2_dbg(1, debug, sd,
- "TDA9840_DETECT: register contents invalid\n");
- return -EINVAL;
- }
+ if (stat < 0)
+ return stat;
- v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte);
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
- switch (byte & 0x60) {
+ switch (stat & 0x60) {
case 0x00:
t->rxsubchans = V4L2_TUNER_SUB_MONO;
break;
diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c
index a794ae62aeb..bfbf9e56b0a 100644
--- a/drivers/media/video/tlg2300/pd-video.c
+++ b/drivers/media/video/tlg2300/pd-video.c
@@ -150,7 +150,6 @@ static int vidioc_querycap(struct file *file, void *fh,
strcpy(cap->driver, "tele-video");
strcpy(cap->card, "Telegent Poseidon");
usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info));
- cap->version = KERNEL_VERSION(0, 0, 1);
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
V4L2_CAP_AUDIO | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE;
diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c
index 859eb90e4d5..e80b7e19047 100644
--- a/drivers/media/video/tm6000/tm6000-input.c
+++ b/drivers/media/video/tm6000/tm6000-input.c
@@ -168,7 +168,6 @@ static void tm6000_ir_urb_received(struct urb *urb)
struct tm6000_IR *ir = dev->ir;
struct tm6000_ir_poll_result poll_result;
char *buf;
- int rc;
dprintk(2, "%s\n",__func__);
if (urb->status < 0 || urb->actual_length <= 0) {
@@ -192,7 +191,7 @@ static void tm6000_ir_urb_received(struct urb *urb)
dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
rc_keydown(ir->rc, poll_result.rc_data, 0);
- rc = usb_submit_urb(urb, GFP_ATOMIC);
+ usb_submit_urb(urb, GFP_ATOMIC);
/*
* Flash the led. We can't do it here, as it is running on IRQ context.
* So, use the scheduler to do it, in a few ms.
diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c
index 9dc0831d813..5e28d6a2412 100644
--- a/drivers/media/video/tm6000/tm6000-stds.c
+++ b/drivers/media/video/tm6000/tm6000-stds.c
@@ -338,7 +338,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev)
uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */
uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */
uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */
- uint8_t nicam_flag = 0; /* No NICAM */
if (dev->radio) {
tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
@@ -398,7 +397,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev)
} else {
areg_05 = 0x07;
}
- nicam_flag = 1;
break;
/* other */
case 3:
diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c
index bc13db736e2..f7034df94e0 100644
--- a/drivers/media/video/tm6000/tm6000-video.c
+++ b/drivers/media/video/tm6000/tm6000-video.c
@@ -169,7 +169,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q,
struct tm6000_buffer **buf)
{
struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
- char *outp;
if (list_empty(&dma_q->active)) {
dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n");
@@ -179,11 +178,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q,
*buf = list_entry(dma_q->active.next,
struct tm6000_buffer, vb.queue);
-
- /* Cleans up buffer - Useful for testing for frame/URB loss */
- outp = videobuf_to_vmalloc(&(*buf)->vb);
-
- return;
}
/*
@@ -211,7 +205,7 @@ static int copy_streams(u8 *data, unsigned long len,
{
struct tm6000_dmaqueue *dma_q = urb->context;
struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq);
- u8 *ptr = data, *endp = data+len, c;
+ u8 *ptr = data, *endp = data+len;
unsigned long header = 0;
int rc = 0;
unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0;
@@ -264,7 +258,6 @@ static int copy_streams(u8 *data, unsigned long len,
}
/* split the header fields */
- c = (header >> 24) & 0xff;
size = ((header & 0x7e) << 1);
if (size > 0)
size -= 4;
@@ -889,7 +882,6 @@ static int vidioc_querycap(struct file *file, void *priv,
strlcpy(cap->driver, "tm6000", sizeof(cap->driver));
strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card));
- cap->version = TM6000_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING |
V4L2_CAP_AUDIO |
@@ -1732,6 +1724,10 @@ static struct video_device *vdev_init(struct tm6000_core *dev,
vfd->release = video_device_release;
vfd->debug = tm6000_debug;
vfd->lock = &dev->lock;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags);
snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h
index 27ba659cfa8..6df418658c9 100644
--- a/drivers/media/video/tm6000/tm6000.h
+++ b/drivers/media/video/tm6000/tm6000.h
@@ -33,8 +33,6 @@
#include "dvb_frontend.h"
#include "dmxdev.h"
-#define TM6000_VERSION KERNEL_VERSION(0, 0, 2)
-
/* Inputs */
enum tm6000_itype {
TM6000_INPUT_TV = 1,
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index a5c6397ad59..3e050e12153 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -1241,8 +1241,10 @@ static int tuner_log_status(struct v4l2_subdev *sd)
return 0;
}
-static int tuner_suspend(struct i2c_client *c, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int tuner_suspend(struct device *dev)
{
+ struct i2c_client *c = to_i2c_client(dev);
struct tuner *t = to_tuner(i2c_get_clientdata(c));
struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
@@ -1254,8 +1256,9 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state)
return 0;
}
-static int tuner_resume(struct i2c_client *c)
+static int tuner_resume(struct device *dev)
{
+ struct i2c_client *c = to_i2c_client(dev);
struct tuner *t = to_tuner(i2c_get_clientdata(c));
tuner_dbg("resume\n");
@@ -1266,6 +1269,7 @@ static int tuner_resume(struct i2c_client *c)
return 0;
}
+#endif
static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg)
{
@@ -1310,6 +1314,10 @@ static const struct v4l2_subdev_ops tuner_ops = {
* I2C structs and module init functions
*/
+static const struct dev_pm_ops tuner_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tuner_suspend, tuner_resume)
+};
+
static const struct i2c_device_id tuner_id[] = {
{ "tuner", }, /* autodetect */
{ }
@@ -1320,12 +1328,11 @@ static struct i2c_driver tuner_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "tuner",
+ .pm = &tuner_pm_ops,
},
.probe = tuner_probe,
.remove = tuner_remove,
.command = tuner_command,
- .suspend = tuner_suspend,
- .resume = tuner_resume,
.id_table = tuner_id,
};
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index 1326e11cf4a..b7867427e5c 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -822,7 +822,7 @@ static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
if (index)
return -EINVAL;
- *code = V4L2_MBUS_FMT_YUYV8_2X8;
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
return 0;
}
@@ -830,23 +830,16 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *f)
{
struct tvp5150 *decoder = to_tvp5150(sd);
- v4l2_std_id std;
if (f == NULL)
return -EINVAL;
tvp5150_reset(sd, 0);
- /* Calculate height and width based on current standard */
- if (decoder->norm == V4L2_STD_ALL)
- std = tvp5150_read_std(sd);
- else
- std = decoder->norm;
-
f->width = decoder->rect.width;
f->height = decoder->rect.height;
- f->code = V4L2_MBUS_FMT_YUYV8_2X8;
+ f->code = V4L2_MBUS_FMT_UYVY8_2X8;
f->field = V4L2_FIELD_SEQ_TB;
f->colorspace = V4L2_COLORSPACE_SMPTE170M;
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index d7676d85c4d..fb6a5b57eb8 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <linux/module.h>
+#include <linux/v4l2-dv-timings.h>
#include <media/tvp7002.h>
#include <media/v4l2-device.h>
#include <media/v4l2-chip-ident.h>
@@ -328,6 +329,7 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = {
/* Preset definition for handling device operation */
struct tvp7002_preset_definition {
u32 preset;
+ struct v4l2_dv_timings timings;
const struct i2c_reg_value *p_settings;
enum v4l2_colorspace color_space;
enum v4l2_field scanmode;
@@ -341,6 +343,7 @@ struct tvp7002_preset_definition {
static const struct tvp7002_preset_definition tvp7002_presets[] = {
{
V4L2_DV_720P60,
+ V4L2_DV_BT_CEA_1280X720P60,
tvp7002_parms_720P60,
V4L2_COLORSPACE_REC709,
V4L2_FIELD_NONE,
@@ -351,6 +354,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {
},
{
V4L2_DV_1080I60,
+ V4L2_DV_BT_CEA_1920X1080I60,
tvp7002_parms_1080I60,
V4L2_COLORSPACE_REC709,
V4L2_FIELD_INTERLACED,
@@ -361,6 +365,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {
},
{
V4L2_DV_1080I50,
+ V4L2_DV_BT_CEA_1920X1080I50,
tvp7002_parms_1080I50,
V4L2_COLORSPACE_REC709,
V4L2_FIELD_INTERLACED,
@@ -371,6 +376,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {
},
{
V4L2_DV_720P50,
+ V4L2_DV_BT_CEA_1280X720P50,
tvp7002_parms_720P50,
V4L2_COLORSPACE_REC709,
V4L2_FIELD_NONE,
@@ -381,6 +387,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {
},
{
V4L2_DV_1080P60,
+ V4L2_DV_BT_CEA_1920X1080P60,
tvp7002_parms_1080P60,
V4L2_COLORSPACE_REC709,
V4L2_FIELD_NONE,
@@ -391,6 +398,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {
},
{
V4L2_DV_480P59_94,
+ V4L2_DV_BT_CEA_720X480P59_94,
tvp7002_parms_480P,
V4L2_COLORSPACE_SMPTE170M,
V4L2_FIELD_NONE,
@@ -401,6 +409,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = {
},
{
V4L2_DV_576P50,
+ V4L2_DV_BT_CEA_720X576P50,
tvp7002_parms_576P,
V4L2_COLORSPACE_SMPTE170M,
V4L2_FIELD_NONE,
@@ -605,6 +614,35 @@ static int tvp7002_s_dv_preset(struct v4l2_subdev *sd,
return -EINVAL;
}
+static int tvp7002_s_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *dv_timings)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+ const struct v4l2_bt_timings *bt = &dv_timings->bt;
+ int i;
+
+ if (dv_timings->type != V4L2_DV_BT_656_1120)
+ return -EINVAL;
+ for (i = 0; i < NUM_PRESETS; i++) {
+ const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt;
+
+ if (!memcmp(bt, t, &bt->standards - &bt->width)) {
+ device->current_preset = &tvp7002_presets[i];
+ return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings);
+ }
+ }
+ return -EINVAL;
+}
+
+static int tvp7002_g_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *dv_timings)
+{
+ struct tvp7002 *device = to_tvp7002(sd);
+
+ *dv_timings = device->current_preset->timings;
+ return 0;
+}
+
/*
* tvp7002_s_ctrl() - Set a control
* @ctrl: ptr to v4l2_ctrl struct
@@ -666,11 +704,9 @@ static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f
* Returns the current DV preset by TVP7002. If no active input is
* detected, returns -EINVAL
*/
-static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
- struct v4l2_dv_preset *qpreset)
+static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index)
{
const struct tvp7002_preset_definition *presets = tvp7002_presets;
- struct tvp7002 *device;
u8 progressive;
u32 lpfr;
u32 cpln;
@@ -679,12 +715,9 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
u8 lpf_msb;
u8 cpl_lsb;
u8 cpl_msb;
- int index;
-
- /* Return invalid preset if no active input is detected */
- qpreset->preset = V4L2_DV_INVALID;
- device = to_tvp7002(sd);
+ /* Return invalid index if no active input is detected */
+ *index = NUM_PRESETS;
/* Read standards from device registers */
tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error);
@@ -705,8 +738,8 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT;
/* Do checking of video modes */
- for (index = 0; index < NUM_PRESETS; index++, presets++)
- if (lpfr == presets->lines_per_frame &&
+ for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++)
+ if (lpfr == presets->lines_per_frame &&
progressive == presets->progressive) {
if (presets->cpl_min == 0xffff)
break;
@@ -714,17 +747,42 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
break;
}
- if (index == NUM_PRESETS) {
+ if (*index == NUM_PRESETS) {
v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n",
lpfr, cpln);
- return 0;
+ return -ENOLINK;
}
- /* Set values in found preset */
- qpreset->preset = presets->preset;
-
/* Update lines per frame and clocks per line info */
- v4l2_dbg(1, debug, sd, "detected preset: %d\n", presets->preset);
+ v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index);
+ return 0;
+}
+
+static int tvp7002_query_dv_preset(struct v4l2_subdev *sd,
+ struct v4l2_dv_preset *qpreset)
+{
+ int index;
+ int err = tvp7002_query_dv(sd, &index);
+
+ if (err || index == NUM_PRESETS) {
+ qpreset->preset = V4L2_DV_INVALID;
+ if (err == -ENOLINK)
+ err = 0;
+ return err;
+ }
+ qpreset->preset = tvp7002_presets[index].preset;
+ return 0;
+}
+
+static int tvp7002_query_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings *timings)
+{
+ int index;
+ int err = tvp7002_query_dv(sd, &index);
+
+ if (err)
+ return err;
+ *timings = tvp7002_presets[index].timings;
return 0;
}
@@ -894,6 +952,17 @@ static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd,
return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset);
}
+static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ /* Check requested format index is within range */
+ if (timings->index >= NUM_PRESETS)
+ return -EINVAL;
+
+ timings->timings = tvp7002_presets[timings->index].timings;
+ return 0;
+}
+
static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = {
.s_ctrl = tvp7002_s_ctrl,
};
@@ -920,6 +989,10 @@ static const struct v4l2_subdev_video_ops tvp7002_video_ops = {
.enum_dv_presets = tvp7002_enum_dv_presets,
.s_dv_preset = tvp7002_s_dv_preset,
.query_dv_preset = tvp7002_query_dv_preset,
+ .g_dv_timings = tvp7002_g_dv_timings,
+ .s_dv_timings = tvp7002_s_dv_timings,
+ .enum_dv_timings = tvp7002_enum_dv_timings,
+ .query_dv_timings = tvp7002_query_dv_timings,
.s_stream = tvp7002_s_stream,
.g_mbus_fmt = tvp7002_mbus_fmt,
.try_mbus_fmt = tvp7002_mbus_fmt,
diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
index f344411a457..c9b2042f8bd 100644
--- a/drivers/media/video/usbvision/usbvision-core.c
+++ b/drivers/media/video/usbvision/usbvision-core.c
@@ -601,13 +601,12 @@ static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char *
unsigned char *decompressed, int *start_pos,
int *block_typestart_pos, int len)
{
- int rest_pixel, idx, max_pos, pos, extra_pos, block_len, block_type_pos, block_type_len;
+ int rest_pixel, idx, pos, extra_pos, block_len, block_type_pos, block_type_len;
unsigned char block_byte, block_code, block_type, block_type_byte, integrator;
integrator = 0;
pos = *start_pos;
block_type_pos = *block_typestart_pos;
- max_pos = 396; /* pos + len; */
extra_pos = pos;
block_len = 0;
block_byte = 0;
@@ -702,7 +701,7 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision
unsigned char strip_data[USBVISION_STRIP_LEN_MAX];
unsigned char strip_header[USBVISION_STRIP_HEADER_LEN];
int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos;
- int clipmask_index, bytes_per_pixel, rc;
+ int clipmask_index;
int image_size;
unsigned char rv, gv, bv;
static unsigned char *Y, *U, *V;
@@ -769,7 +768,6 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision
return parse_state_next_frame;
}
- bytes_per_pixel = frame->v4l2_format.bytes_per_pixel;
clipmask_index = frame->curline * MAX_FRAME_WIDTH;
scratch_get(usbvision, strip_data, strip_len);
@@ -781,14 +779,14 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision
usbvision->block_pos = block_pos;
- rc = usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end);
+ usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end);
if (strip_len > usbvision->max_strip_len)
usbvision->max_strip_len = strip_len;
if (frame->curline % 2)
- rc = usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2);
+ usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2);
else
- rc = usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2);
+ usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2);
if (block_pos > usbvision->comprblock_pos)
usbvision->comprblock_pos = block_pos;
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index 5a74f5e07d7..9bd8f084f34 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -1296,6 +1296,10 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
if (NULL == vdev)
return NULL;
*vdev = *vdev_template;
+ /* Locking in file operations other than ioctl should be done
+ by the driver, not the V4L2 core.
+ This driver needs auditing so that this flag can be removed. */
+ set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags);
vdev->lock = &usbvision->v4l2_lock;
vdev->v4l2_dev = &usbvision->v4l2_dev;
snprintf(vdev->name, sizeof(vdev->name), "%s", name);
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
index 0efd3b10b35..af26bbe6f76 100644
--- a/drivers/media/video/uvc/uvc_ctrl.c
+++ b/drivers/media/video/uvc/uvc_ctrl.c
@@ -21,6 +21,7 @@
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/atomic.h>
+#include <media/v4l2-ctrls.h>
#include "uvcvideo.h"
@@ -420,6 +421,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ .master_id = V4L2_CID_HUE_AUTO,
+ .master_manual = 0,
},
{
.id = V4L2_CID_SATURATION,
@@ -492,6 +495,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ .slave_ids = { V4L2_CID_HUE, },
},
{
.id = V4L2_CID_EXPOSURE_AUTO,
@@ -504,6 +508,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.data_type = UVC_CTRL_DATA_TYPE_BITMASK,
.menu_info = exposure_auto_controls,
.menu_count = ARRAY_SIZE(exposure_auto_controls),
+ .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, },
},
{
.id = V4L2_CID_EXPOSURE_AUTO_PRIORITY,
@@ -524,6 +529,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ .master_id = V4L2_CID_EXPOSURE_AUTO,
+ .master_manual = V4L2_EXPOSURE_MANUAL,
},
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
@@ -534,6 +541,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ .slave_ids = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, },
},
{
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
@@ -544,6 +552,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ .master_id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .master_manual = 0,
},
{
.id = V4L2_CID_AUTO_WHITE_BALANCE,
@@ -554,6 +564,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ .slave_ids = { V4L2_CID_BLUE_BALANCE,
+ V4L2_CID_RED_BALANCE },
},
{
.id = V4L2_CID_BLUE_BALANCE,
@@ -564,6 +576,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ .master_id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .master_manual = 0,
},
{
.id = V4L2_CID_RED_BALANCE,
@@ -574,6 +588,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 16,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ .master_id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .master_manual = 0,
},
{
.id = V4L2_CID_FOCUS_ABSOLUTE,
@@ -584,6 +600,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ .master_id = V4L2_CID_FOCUS_AUTO,
+ .master_manual = 0,
},
{
.id = V4L2_CID_FOCUS_AUTO,
@@ -594,6 +612,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_BOOLEAN,
.data_type = UVC_CTRL_DATA_TYPE_BOOLEAN,
+ .slave_ids = { V4L2_CID_FOCUS_ABSOLUTE, },
},
{
.id = V4L2_CID_IRIS_ABSOLUTE,
@@ -899,25 +918,54 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain,
return 0;
}
-int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
- struct v4l2_queryctrl *v4l2_ctrl)
+static int __uvc_ctrl_get(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
+ s32 *value)
{
- struct uvc_control *ctrl;
- struct uvc_control_mapping *mapping;
struct uvc_menu_info *menu;
unsigned int i;
int ret;
- ret = mutex_lock_interruptible(&chain->ctrl_mutex);
- if (ret < 0)
- return -ERESTARTSYS;
+ if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
+ return -EINVAL;
- ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
- if (ctrl == NULL) {
- ret = -EINVAL;
- goto done;
+ if (!ctrl->loaded) {
+ ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
+ chain->dev->intfnum, ctrl->info.selector,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
+ ctrl->info.size);
+ if (ret < 0)
+ return ret;
+
+ ctrl->loaded = 1;
}
+ *value = mapping->get(mapping, UVC_GET_CUR,
+ uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
+
+ if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
+ menu = mapping->menu_info;
+ for (i = 0; i < mapping->menu_count; ++i, ++menu) {
+ if (menu->value == *value) {
+ *value = i;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
+ struct uvc_control *ctrl,
+ struct uvc_control_mapping *mapping,
+ struct v4l2_queryctrl *v4l2_ctrl)
+{
+ struct uvc_control_mapping *master_map = NULL;
+ struct uvc_control *master_ctrl = NULL;
+ struct uvc_menu_info *menu;
+ unsigned int i;
+
memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl);
v4l2_ctrl->id = mapping->id;
v4l2_ctrl->type = mapping->v4l2_type;
@@ -929,10 +977,23 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR))
v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if (mapping->master_id)
+ __uvc_find_control(ctrl->entity, mapping->master_id,
+ &master_map, &master_ctrl, 0);
+ if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
+ s32 val;
+ int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val != mapping->master_manual)
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ }
+
if (!ctrl->cached) {
- ret = uvc_ctrl_populate_cache(chain, ctrl);
+ int ret = uvc_ctrl_populate_cache(chain, ctrl);
if (ret < 0)
- goto done;
+ return ret;
}
if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) {
@@ -954,19 +1015,19 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
}
}
- goto done;
+ return 0;
case V4L2_CTRL_TYPE_BOOLEAN:
v4l2_ctrl->minimum = 0;
v4l2_ctrl->maximum = 1;
v4l2_ctrl->step = 1;
- goto done;
+ return 0;
case V4L2_CTRL_TYPE_BUTTON:
v4l2_ctrl->minimum = 0;
v4l2_ctrl->maximum = 0;
v4l2_ctrl->step = 0;
- goto done;
+ return 0;
default:
break;
@@ -984,6 +1045,27 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES));
+ return 0;
+}
+
+int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
+ struct v4l2_queryctrl *v4l2_ctrl)
+{
+ struct uvc_control *ctrl;
+ struct uvc_control_mapping *mapping;
+ int ret;
+
+ ret = mutex_lock_interruptible(&chain->ctrl_mutex);
+ if (ret < 0)
+ return -ERESTARTSYS;
+
+ ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping);
+ if (ctrl == NULL) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl);
done:
mutex_unlock(&chain->ctrl_mutex);
return ret;
@@ -1054,6 +1136,174 @@ done:
return ret;
}
+/* --------------------------------------------------------------------------
+ * Ctrl event handling
+ */
+
+static void uvc_ctrl_fill_event(struct uvc_video_chain *chain,
+ struct v4l2_event *ev,
+ struct uvc_control *ctrl,
+ struct uvc_control_mapping *mapping,
+ s32 value, u32 changes)
+{
+ struct v4l2_queryctrl v4l2_ctrl;
+
+ __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl);
+
+ memset(ev->reserved, 0, sizeof(ev->reserved));
+ ev->type = V4L2_EVENT_CTRL;
+ ev->id = v4l2_ctrl.id;
+ ev->u.ctrl.value = value;
+ ev->u.ctrl.changes = changes;
+ ev->u.ctrl.type = v4l2_ctrl.type;
+ ev->u.ctrl.flags = v4l2_ctrl.flags;
+ ev->u.ctrl.minimum = v4l2_ctrl.minimum;
+ ev->u.ctrl.maximum = v4l2_ctrl.maximum;
+ ev->u.ctrl.step = v4l2_ctrl.step;
+ ev->u.ctrl.default_value = v4l2_ctrl.default_value;
+}
+
+static void uvc_ctrl_send_event(struct uvc_fh *handle,
+ struct uvc_control *ctrl, struct uvc_control_mapping *mapping,
+ s32 value, u32 changes)
+{
+ struct v4l2_subscribed_event *sev;
+ struct v4l2_event ev;
+
+ if (list_empty(&mapping->ev_subs))
+ return;
+
+ uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes);
+
+ list_for_each_entry(sev, &mapping->ev_subs, node) {
+ if (sev->fh && (sev->fh != &handle->vfh ||
+ (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) ||
+ (changes & V4L2_EVENT_CTRL_CH_FLAGS)))
+ v4l2_event_queue_fh(sev->fh, &ev);
+ }
+}
+
+static void uvc_ctrl_send_slave_event(struct uvc_fh *handle,
+ struct uvc_control *master, u32 slave_id,
+ const struct v4l2_ext_control *xctrls, unsigned int xctrls_count)
+{
+ struct uvc_control_mapping *mapping = NULL;
+ struct uvc_control *ctrl = NULL;
+ u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+ unsigned int i;
+ s32 val = 0;
+
+ /*
+ * We can skip sending an event for the slave if the slave
+ * is being modified in the same transaction.
+ */
+ for (i = 0; i < xctrls_count; i++) {
+ if (xctrls[i].id == slave_id)
+ return;
+ }
+
+ __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0);
+ if (ctrl == NULL)
+ return;
+
+ if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
+ changes |= V4L2_EVENT_CTRL_CH_VALUE;
+
+ uvc_ctrl_send_event(handle, ctrl, mapping, val, changes);
+}
+
+static void uvc_ctrl_send_events(struct uvc_fh *handle,
+ const struct v4l2_ext_control *xctrls, unsigned int xctrls_count)
+{
+ struct uvc_control_mapping *mapping;
+ struct uvc_control *ctrl;
+ u32 changes = V4L2_EVENT_CTRL_CH_VALUE;
+ unsigned int i;
+ unsigned int j;
+
+ for (i = 0; i < xctrls_count; ++i) {
+ ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping);
+
+ for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) {
+ if (!mapping->slave_ids[j])
+ break;
+ uvc_ctrl_send_slave_event(handle, ctrl,
+ mapping->slave_ids[j],
+ xctrls, xctrls_count);
+ }
+
+ /*
+ * If the master is being modified in the same transaction
+ * flags may change too.
+ */
+ if (mapping->master_id) {
+ for (j = 0; j < xctrls_count; j++) {
+ if (xctrls[j].id == mapping->master_id) {
+ changes |= V4L2_EVENT_CTRL_CH_FLAGS;
+ break;
+ }
+ }
+ }
+
+ uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value,
+ changes);
+ }
+}
+
+static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
+{
+ struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh);
+ struct uvc_control_mapping *mapping;
+ struct uvc_control *ctrl;
+ int ret;
+
+ ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex);
+ if (ret < 0)
+ return -ERESTARTSYS;
+
+ ctrl = uvc_find_control(handle->chain, sev->id, &mapping);
+ if (ctrl == NULL) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ list_add_tail(&sev->node, &mapping->ev_subs);
+ if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) {
+ struct v4l2_event ev;
+ u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
+ s32 val = 0;
+
+ if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
+ changes |= V4L2_EVENT_CTRL_CH_VALUE;
+
+ uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
+ changes);
+ /* Mark the queue as active, allowing this initial
+ event to be accepted. */
+ sev->elems = elems;
+ v4l2_event_queue_fh(sev->fh, &ev);
+ }
+
+done:
+ mutex_unlock(&handle->chain->ctrl_mutex);
+ return ret;
+}
+
+static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev)
+{
+ struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh);
+
+ mutex_lock(&handle->chain->ctrl_mutex);
+ list_del(&sev->node);
+ mutex_unlock(&handle->chain->ctrl_mutex);
+}
+
+const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = {
+ .add = uvc_ctrl_add_event,
+ .del = uvc_ctrl_del_event,
+ .replace = v4l2_ctrl_replace,
+ .merge = v4l2_ctrl_merge,
+};
/* --------------------------------------------------------------------------
* Control transactions
@@ -1101,9 +1351,12 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
/* Reset the loaded flag for auto-update controls that were
* marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent
- * uvc_ctrl_get from using the cached value.
+ * uvc_ctrl_get from using the cached value, and for write-only
+ * controls to prevent uvc_ctrl_set from setting bits not
+ * explicitly set by the user.
*/
- if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE)
+ if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE ||
+ !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
ctrl->loaded = 0;
if (!ctrl->dirty)
@@ -1131,8 +1384,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
return 0;
}
-int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
+int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
+ const struct v4l2_ext_control *xctrls,
+ unsigned int xctrls_count)
{
+ struct uvc_video_chain *chain = handle->chain;
struct uvc_entity *entity;
int ret = 0;
@@ -1143,6 +1399,8 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback)
goto done;
}
+ if (!rollback)
+ uvc_ctrl_send_events(handle, xctrls, xctrls_count);
done:
mutex_unlock(&chain->ctrl_mutex);
return ret;
@@ -1153,39 +1411,12 @@ int uvc_ctrl_get(struct uvc_video_chain *chain,
{
struct uvc_control *ctrl;
struct uvc_control_mapping *mapping;
- struct uvc_menu_info *menu;
- unsigned int i;
- int ret;
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
- if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
+ if (ctrl == NULL)
return -EINVAL;
- if (!ctrl->loaded) {
- ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
- chain->dev->intfnum, ctrl->info.selector,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
- ctrl->info.size);
- if (ret < 0)
- return ret;
-
- ctrl->loaded = 1;
- }
-
- xctrl->value = mapping->get(mapping, UVC_GET_CUR,
- uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
-
- if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
- menu = mapping->menu_info;
- for (i = 0; i < mapping->menu_count; ++i, ++menu) {
- if (menu->value == xctrl->value) {
- xctrl->value = i;
- break;
- }
- }
- }
-
- return 0;
+ return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value);
}
int uvc_ctrl_set(struct uvc_video_chain *chain,
@@ -1641,6 +1872,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
if (map == NULL)
return -ENOMEM;
+ INIT_LIST_HEAD(&map->ev_subs);
+
size = sizeof(*mapping->menu_info) * mapping->menu_count;
map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
if (map->menu_info == NULL) {
@@ -1653,7 +1886,6 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
if (map->set == NULL)
map->set = uvc_set_le_value;
- map->ctrl = &ctrl->info;
list_add_tail(&map->list, &ctrl->info.mappings);
uvc_trace(UVC_TRACE_CONTROL,
"Adding mapping '%s' to control %pUl/%u.\n",
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 8f54e24e3f3..9288fbd5001 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -207,6 +207,19 @@ int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
return ret;
}
+#ifndef CONFIG_MMU
+unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
+ unsigned long pgoff)
+{
+ unsigned long ret;
+
+ mutex_lock(&queue->mutex);
+ ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
+ mutex_unlock(&queue->mutex);
+ return ret;
+}
+#endif
+
unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
poll_table *wait)
{
@@ -237,36 +250,6 @@ int uvc_queue_allocated(struct uvc_video_queue *queue)
return allocated;
}
-#ifndef CONFIG_MMU
-/*
- * Get unmapped area.
- *
- * NO-MMU arch need this function to make mmap() work correctly.
- */
-unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff)
-{
- struct uvc_buffer *buffer;
- unsigned int i;
- unsigned long ret;
-
- mutex_lock(&queue->mutex);
- for (i = 0; i < queue->count; ++i) {
- buffer = &queue->buffer[i];
- if ((buffer->buf.m.offset >> PAGE_SHIFT) == pgoff)
- break;
- }
- if (i == queue->count) {
- ret = -EINVAL;
- goto done;
- }
- ret = (unsigned long)buf->mem;
-done:
- mutex_unlock(&queue->mutex);
- return ret;
-}
-#endif
-
/*
* Enable or disable the video buffers queue.
*
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index ff2cdddf9bc..759bef8897e 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -25,6 +25,8 @@
#include <linux/atomic.h>
#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include "uvcvideo.h"
@@ -505,6 +507,8 @@ static int uvc_v4l2_open(struct file *file)
}
}
+ v4l2_fh_init(&handle->vfh, stream->vdev);
+ v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;
handle->stream = stream;
handle->state = UVC_HANDLE_PASSIVE;
@@ -528,6 +532,8 @@ static int uvc_v4l2_release(struct file *file)
/* Release the file handle. */
uvc_dismiss_privileges(handle);
+ v4l2_fh_del(&handle->vfh);
+ v4l2_fh_exit(&handle->vfh);
kfree(handle);
file->private_data = NULL;
@@ -584,7 +590,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return ret;
ret = uvc_ctrl_get(chain, &xctrl);
- uvc_ctrl_rollback(chain);
+ uvc_ctrl_rollback(handle);
if (ret >= 0)
ctrl->value = xctrl.value;
break;
@@ -605,10 +611,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ret = uvc_ctrl_set(chain, &xctrl);
if (ret < 0) {
- uvc_ctrl_rollback(chain);
+ uvc_ctrl_rollback(handle);
return ret;
}
- ret = uvc_ctrl_commit(chain);
+ ret = uvc_ctrl_commit(handle, &xctrl, 1);
if (ret == 0)
ctrl->value = xctrl.value;
break;
@@ -630,13 +636,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_get(chain, ctrl);
if (ret < 0) {
- uvc_ctrl_rollback(chain);
+ uvc_ctrl_rollback(handle);
ctrls->error_idx = i;
return ret;
}
}
ctrls->error_idx = 0;
- ret = uvc_ctrl_rollback(chain);
+ ret = uvc_ctrl_rollback(handle);
break;
}
@@ -654,7 +660,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
for (i = 0; i < ctrls->count; ++ctrl, ++i) {
ret = uvc_ctrl_set(chain, ctrl);
if (ret < 0) {
- uvc_ctrl_rollback(chain);
+ uvc_ctrl_rollback(handle);
ctrls->error_idx = i;
return ret;
}
@@ -663,9 +669,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
ctrls->error_idx = 0;
if (cmd == VIDIOC_S_EXT_CTRLS)
- ret = uvc_ctrl_commit(chain);
+ ret = uvc_ctrl_commit(handle,
+ ctrls->controls, ctrls->count);
else
- ret = uvc_ctrl_rollback(chain);
+ ret = uvc_ctrl_rollback(handle);
break;
}
@@ -687,7 +694,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
break;
}
pin = iterm->id;
- } else if (pin < selector->bNrInPins) {
+ } else if (index < selector->bNrInPins) {
pin = selector->baSourceID[index];
list_for_each_entry(iterm, &chain->entities, chain) {
if (!UVC_ENTITY_IS_ITERM(iterm))
@@ -990,6 +997,26 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return uvc_video_enable(stream, 0);
}
+ case VIDIOC_SUBSCRIBE_EVENT:
+ {
+ struct v4l2_event_subscription *sub = arg;
+
+ switch (sub->type) {
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(&handle->vfh, sub, 0,
+ &uvc_ctrl_sub_ev_ops);
+ default:
+ return -EINVAL;
+ }
+ }
+
+ case VIDIOC_UNSUBSCRIBE_EVENT:
+ return v4l2_event_unsubscribe(&handle->vfh, arg);
+
+ case VIDIOC_DQEVENT:
+ return v4l2_event_dequeue(&handle->vfh, arg,
+ file->f_flags & O_NONBLOCK);
+
/* Analog video standards make no sense for digital cameras. */
case VIDIOC_ENUMSTD:
case VIDIOC_QUERYSTD:
@@ -1097,7 +1124,8 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
__put_user(kp->menu_count, &up->menu_count))
return -EFAULT;
- __clear_user(up->reserved, sizeof(up->reserved));
+ if (__clear_user(up->reserved, sizeof(up->reserved)))
+ return -EFAULT;
if (kp->menu_count == 0)
return 0;
@@ -1105,8 +1133,6 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
if (get_user(p, &up->menu_info))
return -EFAULT;
umenus = compat_ptr(p);
- if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus)))
- return -EFAULT;
if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus)))
return -EFAULT;
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
index 67f88d85bb1..7c3d082505b 100644
--- a/drivers/media/video/uvc/uvcvideo.h
+++ b/drivers/media/video/uvc/uvcvideo.h
@@ -13,6 +13,8 @@
#include <linux/videodev2.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
#include <media/videobuf2-core.h>
/* --------------------------------------------------------------------------
@@ -153,8 +155,7 @@ struct uvc_control_info {
struct uvc_control_mapping {
struct list_head list;
-
- struct uvc_control_info *ctrl;
+ struct list_head ev_subs;
__u32 id;
__u8 name[32];
@@ -169,6 +170,10 @@ struct uvc_control_mapping {
struct uvc_menu_info *menu_info;
__u32 menu_count;
+ __u32 master_id;
+ __s32 master_manual;
+ __u32 slave_ids[2];
+
__s32 (*get) (struct uvc_control_mapping *mapping, __u8 query,
const __u8 *data);
void (*set) (struct uvc_control_mapping *mapping, __s32 value,
@@ -524,6 +529,7 @@ enum uvc_handle_state {
};
struct uvc_fh {
+ struct v4l2_fh vfh;
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
enum uvc_handle_state state;
@@ -643,6 +649,8 @@ extern int uvc_status_suspend(struct uvc_device *dev);
extern int uvc_status_resume(struct uvc_device *dev);
/* Controls */
+extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops;
+
extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
struct v4l2_queryctrl *v4l2_ctrl);
extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain,
@@ -655,14 +663,18 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
extern int uvc_ctrl_resume_device(struct uvc_device *dev);
extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
-extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
-static inline int uvc_ctrl_commit(struct uvc_video_chain *chain)
+extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback,
+ const struct v4l2_ext_control *xctrls,
+ unsigned int xctrls_count);
+static inline int uvc_ctrl_commit(struct uvc_fh *handle,
+ const struct v4l2_ext_control *xctrls,
+ unsigned int xctrls_count)
{
- return __uvc_ctrl_commit(chain, 0);
+ return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count);
}
-static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain)
+static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
{
- return __uvc_ctrl_commit(chain, 1);
+ return __uvc_ctrl_commit(handle, 1, NULL, 0);
}
extern int uvc_ctrl_get(struct uvc_video_chain *chain,
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index 2829d256e4b..5327ad3a639 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -37,7 +37,7 @@ struct v4l2_clip32 {
struct v4l2_window32 {
struct v4l2_rect w;
- enum v4l2_field field;
+ __u32 field; /* enum v4l2_field */
__u32 chromakey;
compat_caddr_t clips; /* actually struct v4l2_clip32 * */
__u32 clipcount;
@@ -147,7 +147,7 @@ static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp,
}
struct v4l2_format32 {
- enum v4l2_buf_type type;
+ __u32 type; /* enum v4l2_buf_type */
union {
struct v4l2_pix_format pix;
struct v4l2_pix_format_mplane pix_mp;
@@ -170,7 +170,7 @@ struct v4l2_format32 {
struct v4l2_create_buffers32 {
__u32 index;
__u32 count;
- enum v4l2_memory memory;
+ __u32 memory; /* enum v4l2_memory */
struct v4l2_format32 format;
__u32 reserved[8];
};
@@ -311,16 +311,16 @@ struct v4l2_plane32 {
struct v4l2_buffer32 {
__u32 index;
- enum v4l2_buf_type type;
+ __u32 type; /* enum v4l2_buf_type */
__u32 bytesused;
__u32 flags;
- enum v4l2_field field;
+ __u32 field; /* enum v4l2_field */
struct compat_timeval timestamp;
struct v4l2_timecode timecode;
__u32 sequence;
/* memory location */
- enum v4l2_memory memory;
+ __u32 memory; /* enum v4l2_memory */
union {
__u32 offset;
compat_long_t userptr;
@@ -1023,6 +1023,9 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_UNSUBSCRIBE_EVENT:
case VIDIOC_CREATE_BUFS32:
case VIDIOC_PREPARE_BUF32:
+ case VIDIOC_ENUM_DV_TIMINGS:
+ case VIDIOC_QUERY_DV_TIMINGS:
+ case VIDIOC_DV_TIMINGS_CAP:
ret = do_video_ioctl(file, cmd, arg);
break;
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 18015c0a8d3..9abd9abd450 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -230,6 +230,19 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
"Aperture Priority Mode",
NULL
};
+ static const char * const camera_exposure_metering[] = {
+ "Average",
+ "Center Weighted",
+ "Spot",
+ NULL
+ };
+ static const char * const camera_auto_focus_range[] = {
+ "Auto",
+ "Normal",
+ "Macro",
+ "Infinity",
+ NULL
+ };
static const char * const colorfx[] = {
"None",
"Black & White",
@@ -241,6 +254,47 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
"Grass Green",
"Skin Whiten",
"Vivid",
+ "Aqua",
+ "Art Freeze",
+ "Silhouette",
+ "Solarization",
+ "Antique",
+ "Set Cb/Cr",
+ NULL
+ };
+ static const char * const auto_n_preset_white_balance[] = {
+ "Manual",
+ "Auto",
+ "Incandescent",
+ "Fluorescent",
+ "Fluorescent H",
+ "Horizon",
+ "Daylight",
+ "Flash",
+ "Cloudy",
+ "Shade",
+ NULL,
+ };
+ static const char * const camera_iso_sensitivity_auto[] = {
+ "Manual",
+ "Auto",
+ NULL
+ };
+ static const char * const scene_mode[] = {
+ "None",
+ "Backlight",
+ "Beach/Snow",
+ "Candle Light",
+ "Dusk/Dawn",
+ "Fall Colors",
+ "Fireworks",
+ "Landscape",
+ "Night",
+ "Party/Indoor",
+ "Portrait",
+ "Sports",
+ "Sunset",
+ "Text",
NULL
};
static const char * const tune_preemphasis[] = {
@@ -410,8 +464,18 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
return camera_power_line_frequency;
case V4L2_CID_EXPOSURE_AUTO:
return camera_exposure_auto;
+ case V4L2_CID_EXPOSURE_METERING:
+ return camera_exposure_metering;
+ case V4L2_CID_AUTO_FOCUS_RANGE:
+ return camera_auto_focus_range;
case V4L2_CID_COLORFX:
return colorfx;
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
+ return auto_n_preset_white_balance;
+ case V4L2_CID_ISO_SENSITIVITY_AUTO:
+ return camera_iso_sensitivity_auto;
+ case V4L2_CID_SCENE_MODE:
+ return scene_mode;
case V4L2_CID_TUNE_PREEMPHASIS:
return tune_preemphasis;
case V4L2_CID_FLASH_LED_MODE:
@@ -493,6 +557,7 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers";
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers";
case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component";
+ case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr";
/* MPEG controls */
/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -590,13 +655,26 @@ const char *v4l2_ctrl_get_name(u32 id)
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_FOCUS_AUTO: return "Focus, Automatic Continuous";
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";
case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute";
case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative";
+ case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias";
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset";
+ case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range";
+ case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization";
+ case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity";
+ case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto";
+ case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode";
+ case V4L2_CID_SCENE_MODE: return "Scene Mode";
+ case V4L2_CID_3A_LOCK: return "3A Lock";
+ case V4L2_CID_AUTO_FOCUS_START: return "Auto Focus, Start";
+ case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop";
+ case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status";
+ case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range";
/* FM Radio Modulator control */
/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -644,6 +722,17 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers";
+ /* Image source controls */
+ case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls";
+ case V4L2_CID_VBLANK: return "Vertical Blanking";
+ case V4L2_CID_HBLANK: return "Horizontal Blanking";
+ case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain";
+
+ /* Image processing controls */
+ case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls";
+ case V4L2_CID_LINK_FREQ: return "Link Frequency";
+ case V4L2_CID_PIXEL_RATE: return "Pixel Rate";
+
default:
return NULL;
}
@@ -688,6 +777,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
+ case V4L2_CID_WIDE_DYNAMIC_RANGE:
+ case V4L2_CID_IMAGE_STABILIZATION:
*type = V4L2_CTRL_TYPE_BOOLEAN;
*min = 0;
*max = *step = 1;
@@ -696,6 +787,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_TILT_RESET:
case V4L2_CID_FLASH_STROBE:
case V4L2_CID_FLASH_STROBE_STOP:
+ case V4L2_CID_AUTO_FOCUS_START:
+ case V4L2_CID_AUTO_FOCUS_STOP:
*type = V4L2_CTRL_TYPE_BUTTON;
*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
*min = *max = *step = *def = 0;
@@ -719,7 +812,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_STREAM_TYPE:
case V4L2_CID_MPEG_STREAM_VBI_FMT:
case V4L2_CID_EXPOSURE_AUTO:
+ case V4L2_CID_AUTO_FOCUS_RANGE:
case V4L2_CID_COLORFX:
+ case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
case V4L2_CID_TUNE_PREEMPHASIS:
case V4L2_CID_FLASH_LED_MODE:
case V4L2_CID_FLASH_STROBE_SOURCE:
@@ -733,18 +828,30 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ case V4L2_CID_ISO_SENSITIVITY_AUTO:
+ case V4L2_CID_EXPOSURE_METERING:
+ case V4L2_CID_SCENE_MODE:
*type = V4L2_CTRL_TYPE_MENU;
break;
+ case V4L2_CID_LINK_FREQ:
+ *type = V4L2_CTRL_TYPE_INTEGER_MENU;
+ break;
case V4L2_CID_RDS_TX_PS_NAME:
case V4L2_CID_RDS_TX_RADIO_TEXT:
*type = V4L2_CTRL_TYPE_STRING;
break;
+ case V4L2_CID_ISO_SENSITIVITY:
+ case V4L2_CID_AUTO_EXPOSURE_BIAS:
+ *type = V4L2_CTRL_TYPE_INTEGER_MENU;
+ break;
case V4L2_CID_USER_CLASS:
case V4L2_CID_CAMERA_CLASS:
case V4L2_CID_MPEG_CLASS:
case V4L2_CID_FM_TX_CLASS:
case V4L2_CID_FLASH_CLASS:
case V4L2_CID_JPEG_CLASS:
+ case V4L2_CID_IMAGE_SOURCE_CLASS:
+ case V4L2_CID_IMAGE_PROC_CLASS:
*type = V4L2_CTRL_TYPE_CTRL_CLASS;
/* You can neither read not write these */
*flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -759,6 +866,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
break;
case V4L2_CID_FLASH_FAULT:
case V4L2_CID_JPEG_ACTIVE_MARKER:
+ case V4L2_CID_3A_LOCK:
+ case V4L2_CID_AUTO_FOCUS_STATUS:
*type = V4L2_CTRL_TYPE_BITMASK;
break;
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
@@ -768,8 +877,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
break;
case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ *flags |= V4L2_CTRL_FLAG_VOLATILE;
+ /* Fall through */
+ case V4L2_CID_PIXEL_RATE:
*type = V4L2_CTRL_TYPE_INTEGER64;
- *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ *min = *max = *step = *def = 0;
break;
default:
*type = V4L2_CTRL_TYPE_INTEGER;
@@ -817,6 +930,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
*flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
break;
case V4L2_CID_FLASH_STROBE_STATUS:
+ case V4L2_CID_AUTO_FOCUS_STATUS:
case V4L2_CID_FLASH_READY:
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
break;
@@ -852,7 +966,8 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change
ev->u.ctrl.value64 = ctrl->cur.val64;
ev->u.ctrl.minimum = ctrl->minimum;
ev->u.ctrl.maximum = ctrl->maximum;
- if (ctrl->type == V4L2_CTRL_TYPE_MENU)
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU
+ || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
ev->u.ctrl.step = 1;
else
ev->u.ctrl.step = ctrl->step;
@@ -1083,10 +1198,13 @@ static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval)
return 0;
case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
if (val < ctrl->minimum || val > ctrl->maximum)
return -ERANGE;
- if (ctrl->qmenu[val][0] == '\0' ||
- (ctrl->menu_skip_mask & (1 << val)))
+ if (ctrl->menu_skip_mask & (1 << val))
+ return -EINVAL;
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU &&
+ ctrl->qmenu[val][0] == '\0')
return -EINVAL;
return 0;
@@ -1114,6 +1232,7 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_BOOLEAN:
case V4L2_CTRL_TYPE_MENU:
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
case V4L2_CTRL_TYPE_BITMASK:
case V4L2_CTRL_TYPE_BUTTON:
case V4L2_CTRL_TYPE_CTRL_CLASS:
@@ -1152,7 +1271,8 @@ static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err)
int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl,
unsigned nr_of_controls_hint)
{
- mutex_init(&hdl->lock);
+ hdl->lock = &hdl->_lock;
+ mutex_init(hdl->lock);
INIT_LIST_HEAD(&hdl->ctrls);
INIT_LIST_HEAD(&hdl->ctrl_refs);
hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8;
@@ -1173,7 +1293,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
if (hdl == NULL || hdl->buckets == NULL)
return;
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
/* Free all nodes */
list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) {
list_del(&ref->node);
@@ -1190,7 +1310,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
hdl->buckets = NULL;
hdl->cached = NULL;
hdl->error = 0;
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
}
EXPORT_SYMBOL(v4l2_ctrl_handler_free);
@@ -1255,9 +1375,9 @@ static struct v4l2_ctrl_ref *find_ref_lock(
struct v4l2_ctrl_ref *ref = NULL;
if (hdl) {
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
ref = find_ref(hdl, id);
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
}
return ref;
}
@@ -1304,7 +1424,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl,
INIT_LIST_HEAD(&new_ref->node);
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
/* Add immediately at the end of the list if the list is empty, or if
the last element in the list has a lower ID.
@@ -1334,7 +1454,7 @@ insert_in_hash:
hdl->buckets[bucket] = new_ref;
unlock:
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
return 0;
}
@@ -1343,7 +1463,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
const struct v4l2_ctrl_ops *ops,
u32 id, const char *name, enum v4l2_ctrl_type type,
s32 min, s32 max, u32 step, s32 def,
- u32 flags, const char * const *qmenu, void *priv)
+ u32 flags, const char * const *qmenu,
+ const s64 *qmenu_int, void *priv)
{
struct v4l2_ctrl *ctrl;
unsigned sz_extra = 0;
@@ -1356,6 +1477,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
(type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
(type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
(type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
+ (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) ||
(type == V4L2_CTRL_TYPE_STRING && max == 0)) {
handler_set_err(hdl, -ERANGE);
return NULL;
@@ -1366,6 +1488,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
}
if ((type == V4L2_CTRL_TYPE_INTEGER ||
type == V4L2_CTRL_TYPE_MENU ||
+ type == V4L2_CTRL_TYPE_INTEGER_MENU ||
type == V4L2_CTRL_TYPE_BOOLEAN) &&
(def < min || def > max)) {
handler_set_err(hdl, -ERANGE);
@@ -1400,7 +1523,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
ctrl->minimum = min;
ctrl->maximum = max;
ctrl->step = step;
- ctrl->qmenu = qmenu;
+ if (type == V4L2_CTRL_TYPE_MENU)
+ ctrl->qmenu = qmenu;
+ else if (type == V4L2_CTRL_TYPE_INTEGER_MENU)
+ ctrl->qmenu_int = qmenu_int;
ctrl->priv = priv;
ctrl->cur.val = ctrl->val = ctrl->default_value = def;
@@ -1414,9 +1540,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
kfree(ctrl);
return NULL;
}
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
list_add_tail(&ctrl->node, &hdl->ctrls);
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
return ctrl;
}
@@ -1427,6 +1553,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl *ctrl;
const char *name = cfg->name;
const char * const *qmenu = cfg->qmenu;
+ const s64 *qmenu_int = cfg->qmenu_int;
enum v4l2_ctrl_type type = cfg->type;
u32 flags = cfg->flags;
s32 min = cfg->min;
@@ -1438,18 +1565,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl,
v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step,
&def, &flags);
- is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU);
+ is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU ||
+ cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU);
if (is_menu)
WARN_ON(step);
else
WARN_ON(cfg->menu_skip_mask);
- if (is_menu && qmenu == NULL)
+ if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL)
qmenu = v4l2_ctrl_get_menu(cfg->id);
+ else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU &&
+ qmenu_int == NULL) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name,
type, min, max,
is_menu ? cfg->menu_skip_mask : step,
- def, flags, qmenu, priv);
+ def, flags, qmenu, qmenu_int, priv);
if (ctrl)
ctrl->is_private = cfg->is_private;
return ctrl;
@@ -1466,12 +1599,13 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl,
u32 flags;
v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
- if (type == V4L2_CTRL_TYPE_MENU) {
+ if (type == V4L2_CTRL_TYPE_MENU
+ || type == V4L2_CTRL_TYPE_INTEGER_MENU) {
handler_set_err(hdl, -EINVAL);
return NULL;
}
return v4l2_ctrl_new(hdl, ops, id, name, type,
- min, max, step, def, flags, NULL, NULL);
+ min, max, step, def, flags, NULL, NULL, NULL);
}
EXPORT_SYMBOL(v4l2_ctrl_new_std);
@@ -1493,10 +1627,31 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl,
return NULL;
}
return v4l2_ctrl_new(hdl, ops, id, name, type,
- 0, max, mask, def, flags, qmenu, NULL);
+ 0, max, mask, def, flags, qmenu, NULL, NULL);
}
EXPORT_SYMBOL(v4l2_ctrl_new_std_menu);
+/* Helper function for standard integer menu controls */
+struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl,
+ const struct v4l2_ctrl_ops *ops,
+ u32 id, s32 max, s32 def, const s64 *qmenu_int)
+{
+ const char *name;
+ enum v4l2_ctrl_type type;
+ s32 min;
+ s32 step;
+ u32 flags;
+
+ v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags);
+ if (type != V4L2_CTRL_TYPE_INTEGER_MENU) {
+ handler_set_err(hdl, -EINVAL);
+ return NULL;
+ }
+ return v4l2_ctrl_new(hdl, ops, id, name, type,
+ 0, max, 0, def, flags, NULL, qmenu_int, NULL);
+}
+EXPORT_SYMBOL(v4l2_ctrl_new_int_menu);
+
/* Add a control from another handler to this handler */
struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl *ctrl)
@@ -1525,7 +1680,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
return 0;
if (hdl->error)
return hdl->error;
- mutex_lock(&add->lock);
+ mutex_lock(add->lock);
list_for_each_entry(ref, &add->ctrl_refs, node) {
struct v4l2_ctrl *ctrl = ref->ctrl;
@@ -1539,7 +1694,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
if (ret)
break;
}
- mutex_unlock(&add->lock);
+ mutex_unlock(add->lock);
return ret;
}
EXPORT_SYMBOL(v4l2_ctrl_add_handler);
@@ -1659,6 +1814,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,
case V4L2_CTRL_TYPE_MENU:
printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
break;
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
+ printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]);
+ break;
case V4L2_CTRL_TYPE_BITMASK:
printk(KERN_CONT "0x%08x", ctrl->cur.val);
break;
@@ -1700,11 +1858,11 @@ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl,
len = strlen(prefix);
if (len && prefix[len - 1] != ' ')
colon = ": ";
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
list_for_each_entry(ctrl, &hdl->ctrls, node)
if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED))
log_ctrl(ctrl, prefix, colon);
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
}
EXPORT_SYMBOL(v4l2_ctrl_handler_log_status);
@@ -1716,7 +1874,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
if (hdl == NULL)
return 0;
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
list_for_each_entry(ctrl, &hdl->ctrls, node)
ctrl->done = false;
@@ -1741,7 +1899,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl)
if (ret)
break;
}
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
return ret;
}
EXPORT_SYMBOL(v4l2_ctrl_handler_setup);
@@ -1756,7 +1914,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
if (hdl == NULL)
return -EINVAL;
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
/* Try to find it */
ref = find_ref(hdl, id);
@@ -1781,7 +1939,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
break;
}
}
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
if (!ref)
return -EINVAL;
@@ -1795,7 +1953,8 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc)
qc->minimum = ctrl->minimum;
qc->maximum = ctrl->maximum;
qc->default_value = ctrl->default_value;
- if (ctrl->type == V4L2_CTRL_TYPE_MENU)
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU
+ || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU)
qc->step = 1;
else
qc->step = ctrl->step;
@@ -1825,16 +1984,33 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm)
qm->reserved = 0;
/* Sanity checks */
- if (ctrl->qmenu == NULL ||
- i < ctrl->minimum || i > ctrl->maximum)
+ switch (ctrl->type) {
+ case V4L2_CTRL_TYPE_MENU:
+ if (ctrl->qmenu == NULL)
+ return -EINVAL;
+ break;
+ case V4L2_CTRL_TYPE_INTEGER_MENU:
+ if (ctrl->qmenu_int == NULL)
+ return -EINVAL;
+ break;
+ default:
return -EINVAL;
+ }
+
+ if (i < ctrl->minimum || i > ctrl->maximum)
+ return -EINVAL;
+
/* Use mask to see if this menu item should be skipped */
if (ctrl->menu_skip_mask & (1 << i))
return -EINVAL;
/* Empty menu items should also be skipped */
- if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
- return -EINVAL;
- strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+ if (ctrl->type == V4L2_CTRL_TYPE_MENU) {
+ if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0')
+ return -EINVAL;
+ strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name));
+ } else {
+ qm->value = ctrl->qmenu_int[i];
+ }
return 0;
}
EXPORT_SYMBOL(v4l2_querymenu);
@@ -1940,7 +2116,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
belong to the same cluster. */
/* This has to be done with the handler lock taken. */
- mutex_lock(&hdl->lock);
+ mutex_lock(hdl->lock);
/* First zero the helper field in the master control references */
for (i = 0; i < cs->count; i++)
@@ -1962,7 +2138,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl,
/* Point the mref helper to the current helper struct. */
mref->helper = h;
}
- mutex_unlock(&hdl->lock);
+ mutex_unlock(hdl->lock);
return 0;
}
@@ -1996,7 +2172,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
return class_check(hdl, cs->ctrl_class);
if (cs->count > ARRAY_SIZE(helper)) {
- helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ helpers = kmalloc_array(cs->count, sizeof(helper[0]),
+ GFP_KERNEL);
if (helpers == NULL)
return -ENOMEM;
}
@@ -2218,7 +2395,8 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
return class_check(hdl, cs->ctrl_class);
if (cs->count > ARRAY_SIZE(helper)) {
- helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL);
+ helpers = kmalloc_array(cs->count, sizeof(helper[0]),
+ GFP_KERNEL);
if (!helpers)
return -ENOMEM;
}
@@ -2381,9 +2559,13 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
}
EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
-void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
- struct v4l2_subscribed_event *sev)
+static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
+
+ if (ctrl == NULL)
+ return -EINVAL;
+
v4l2_ctrl_lock(ctrl);
list_add_tail(&sev->node, &ctrl->ev_subs);
if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
@@ -2394,20 +2576,46 @@ void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY))
changes |= V4L2_EVENT_CTRL_CH_VALUE;
fill_event(&ev, ctrl, changes);
+ /* Mark the queue as active, allowing this initial
+ event to be accepted. */
+ sev->elems = elems;
v4l2_event_queue_fh(sev->fh, &ev);
}
v4l2_ctrl_unlock(ctrl);
+ return 0;
}
-EXPORT_SYMBOL(v4l2_ctrl_add_event);
-void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
- struct v4l2_subscribed_event *sev)
+static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev)
{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id);
+
v4l2_ctrl_lock(ctrl);
list_del(&sev->node);
v4l2_ctrl_unlock(ctrl);
}
-EXPORT_SYMBOL(v4l2_ctrl_del_event);
+
+void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new)
+{
+ u32 old_changes = old->u.ctrl.changes;
+
+ old->u.ctrl = new->u.ctrl;
+ old->u.ctrl.changes |= old_changes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_replace);
+
+void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new)
+{
+ new->u.ctrl.changes |= old->u.ctrl.changes;
+}
+EXPORT_SYMBOL(v4l2_ctrl_merge);
+
+const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = {
+ .add = v4l2_ctrl_add_event,
+ .del = v4l2_ctrl_del_event,
+ .replace = v4l2_ctrl_replace,
+ .merge = v4l2_ctrl_merge,
+};
+EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops);
int v4l2_ctrl_log_status(struct file *file, void *fh)
{
@@ -2425,7 +2633,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
{
if (sub->type == V4L2_EVENT_CTRL)
- return v4l2_event_subscribe(fh, sub, 0);
+ return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops);
return -EINVAL;
}
EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 70bec548d90..5ccbd4629f9 100644
--- a/drivers/media/video/v4l2-dev.c
+++ b/drivers/media/video/v4l2-dev.c
@@ -274,11 +274,12 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf,
if (!vdev->fops->read)
return -EINVAL;
- if (vdev->lock && mutex_lock_interruptible(vdev->lock))
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
+ mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->read(filp, buf, sz, off);
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
return ret;
}
@@ -291,11 +292,12 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf,
if (!vdev->fops->write)
return -EINVAL;
- if (vdev->lock && mutex_lock_interruptible(vdev->lock))
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
+ mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->write(filp, buf, sz, off);
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
return ret;
}
@@ -307,11 +309,11 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll)
if (!vdev->fops->poll)
return DEFAULT_POLLMASK;
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_lock(vdev->lock);
if (video_is_registered(vdev))
ret = vdev->fops->poll(filp, poll);
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
return ret;
}
@@ -322,11 +324,19 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
int ret = -ENODEV;
if (vdev->fops->unlocked_ioctl) {
- if (vdev->lock && mutex_lock_interruptible(vdev->lock))
- return -ERESTARTSYS;
+ bool locked = false;
+
+ if (vdev->lock) {
+ /* always lock unless the cmd is marked as "don't use lock" */
+ locked = !v4l2_is_known_ioctl(cmd) ||
+ !test_bit(_IOC_NR(cmd), vdev->disable_locking);
+
+ if (locked && mutex_lock_interruptible(vdev->lock))
+ return -ERESTARTSYS;
+ }
if (video_is_registered(vdev))
ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
- if (vdev->lock)
+ if (locked)
mutex_unlock(vdev->lock);
} else if (vdev->fops->ioctl) {
/* This code path is a replacement for the BKL. It is a major
@@ -391,11 +401,12 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
if (!vdev->fops->mmap)
return ret;
- if (vdev->lock && mutex_lock_interruptible(vdev->lock))
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
+ mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
if (video_is_registered(vdev))
ret = vdev->fops->mmap(filp, vm);
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
return ret;
}
@@ -418,7 +429,8 @@ static int v4l2_open(struct inode *inode, struct file *filp)
video_get(vdev);
mutex_unlock(&videodev_lock);
if (vdev->fops->open) {
- if (vdev->lock && mutex_lock_interruptible(vdev->lock)) {
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) &&
+ mutex_lock_interruptible(vdev->lock)) {
ret = -ERESTARTSYS;
goto err;
}
@@ -426,7 +438,7 @@ static int v4l2_open(struct inode *inode, struct file *filp)
ret = vdev->fops->open(filp);
else
ret = -ENODEV;
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
}
@@ -444,10 +456,10 @@ static int v4l2_release(struct inode *inode, struct file *filp)
int ret = 0;
if (vdev->fops->release) {
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_lock(vdev->lock);
vdev->fops->release(filp);
- if (vdev->lock)
+ if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags))
mutex_unlock(vdev->lock);
}
/* decrease the refcount unconditionally since the release()
@@ -508,6 +520,175 @@ static int get_index(struct video_device *vdev)
return find_first_zero_bit(used, VIDEO_NUM_DEVICES);
}
+#define SET_VALID_IOCTL(ops, cmd, op) \
+ if (ops->op) \
+ set_bit(_IOC_NR(cmd), valid_ioctls)
+
+/* This determines which ioctls are actually implemented in the driver.
+ It's a one-time thing which simplifies video_ioctl2 as it can just do
+ a bit test.
+
+ Note that drivers can override this by setting bits to 1 in
+ vdev->valid_ioctls. If an ioctl is marked as 1 when this function is
+ called, then that ioctl will actually be marked as unimplemented.
+
+ It does that by first setting up the local valid_ioctls bitmap, and
+ at the end do a:
+
+ vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls)
+ */
+static void determine_valid_ioctls(struct video_device *vdev)
+{
+ DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE);
+ const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
+
+ bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE);
+
+ SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap);
+ if (ops->vidioc_g_priority ||
+ test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
+ set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls);
+ if (ops->vidioc_s_priority ||
+ test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags))
+ set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls);
+ if (ops->vidioc_enum_fmt_vid_cap ||
+ ops->vidioc_enum_fmt_vid_out ||
+ ops->vidioc_enum_fmt_vid_cap_mplane ||
+ ops->vidioc_enum_fmt_vid_out_mplane ||
+ ops->vidioc_enum_fmt_vid_overlay ||
+ ops->vidioc_enum_fmt_type_private)
+ set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls);
+ if (ops->vidioc_g_fmt_vid_cap ||
+ ops->vidioc_g_fmt_vid_out ||
+ ops->vidioc_g_fmt_vid_cap_mplane ||
+ ops->vidioc_g_fmt_vid_out_mplane ||
+ ops->vidioc_g_fmt_vid_overlay ||
+ ops->vidioc_g_fmt_vbi_cap ||
+ ops->vidioc_g_fmt_vid_out_overlay ||
+ ops->vidioc_g_fmt_vbi_out ||
+ ops->vidioc_g_fmt_sliced_vbi_cap ||
+ ops->vidioc_g_fmt_sliced_vbi_out ||
+ ops->vidioc_g_fmt_type_private)
+ set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls);
+ if (ops->vidioc_s_fmt_vid_cap ||
+ ops->vidioc_s_fmt_vid_out ||
+ ops->vidioc_s_fmt_vid_cap_mplane ||
+ ops->vidioc_s_fmt_vid_out_mplane ||
+ ops->vidioc_s_fmt_vid_overlay ||
+ ops->vidioc_s_fmt_vbi_cap ||
+ ops->vidioc_s_fmt_vid_out_overlay ||
+ ops->vidioc_s_fmt_vbi_out ||
+ ops->vidioc_s_fmt_sliced_vbi_cap ||
+ ops->vidioc_s_fmt_sliced_vbi_out ||
+ ops->vidioc_s_fmt_type_private)
+ set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls);
+ if (ops->vidioc_try_fmt_vid_cap ||
+ ops->vidioc_try_fmt_vid_out ||
+ ops->vidioc_try_fmt_vid_cap_mplane ||
+ ops->vidioc_try_fmt_vid_out_mplane ||
+ ops->vidioc_try_fmt_vid_overlay ||
+ ops->vidioc_try_fmt_vbi_cap ||
+ ops->vidioc_try_fmt_vid_out_overlay ||
+ ops->vidioc_try_fmt_vbi_out ||
+ ops->vidioc_try_fmt_sliced_vbi_cap ||
+ ops->vidioc_try_fmt_sliced_vbi_out ||
+ ops->vidioc_try_fmt_type_private)
+ set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
+ SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
+ SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf);
+ SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon);
+ SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
+ if (vdev->tvnorms)
+ set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
+ if (ops->vidioc_g_std || vdev->current_norm)
+ set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std);
+ SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input);
+ SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input);
+ SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output);
+ SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output);
+ SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output);
+ /* Note: the control handler can also be passed through the filehandle,
+ and that can't be tested here. If the bit for these control ioctls
+ is set, then the ioctl is valid. But if it is 0, then it can still
+ be valid if the filehandle passed the control handler. */
+ if (vdev->ctrl_handler || ops->vidioc_queryctrl)
+ set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls)
+ set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls);
+ if (vdev->ctrl_handler || ops->vidioc_querymenu)
+ set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio);
+ SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio);
+ SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout);
+ SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout);
+ SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout);
+ SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator);
+ SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator);
+ if (ops->vidioc_g_crop || ops->vidioc_g_selection)
+ set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls);
+ if (ops->vidioc_s_crop || ops->vidioc_s_selection)
+ set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection);
+ SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection);
+ if (ops->vidioc_cropcap || ops->vidioc_g_selection)
+ set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp);
+ SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp);
+ SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index);
+ SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd);
+ SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd);
+ SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd);
+ if (ops->vidioc_g_parm || vdev->current_norm)
+ set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls);
+ SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm);
+ SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner);
+ SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner);
+ SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency);
+ SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency);
+ SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap);
+ SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status);
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register);
+ SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register);
+#endif
+ SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident);
+ SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals);
+ SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets);
+ SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset);
+ SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset);
+ SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset);
+ SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings);
+ SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings);
+ /* yes, really vidioc_subscribe_event */
+ SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event);
+ SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event);
+ SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event);
+ SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs);
+ SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf);
+ bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls,
+ BASE_VIDIOC_PRIVATE);
+}
+
/**
* __video_register_device - register video4linux devices
* @vdev: video device structure we want to register
@@ -654,6 +835,13 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);
+ /* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is
+ clear and warn if it wasn't. */
+ if (vdev->lock == NULL)
+ WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags));
+
+ if (vdev->ioctl_ops)
+ determine_valid_ioctls(vdev);
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc();
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index c26ad963714..ef2a33c9404 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -25,7 +25,6 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>
-#include <media/v4l2-ctrls.h>
#include <linux/sched.h>
#include <linux/slab.h>
@@ -120,6 +119,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
if (sev == NULL)
return;
+ /*
+ * If the event has been added to the fh->subscribed list, but its
+ * add op has not completed yet elems will be 0, treat this as
+ * not being subscribed.
+ */
+ if (!sev->elems)
+ return;
+
/* Increase event sequence number on fh. */
fh->sequence++;
@@ -132,14 +139,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
sev->first = sev_pos(sev, 1);
fh->navailable--;
if (sev->elems == 1) {
- if (sev->replace) {
- sev->replace(&kev->event, ev);
+ if (sev->ops && sev->ops->replace) {
+ sev->ops->replace(&kev->event, ev);
copy_payload = false;
}
- } else if (sev->merge) {
+ } else if (sev->ops && sev->ops->merge) {
struct v4l2_kevent *second_oldest =
sev->events + sev_pos(sev, 0);
- sev->merge(&kev->event, &second_oldest->event);
+ sev->ops->merge(&kev->event, &second_oldest->event);
}
}
@@ -195,24 +202,11 @@ int v4l2_event_pending(struct v4l2_fh *fh)
}
EXPORT_SYMBOL_GPL(v4l2_event_pending);
-static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new)
-{
- u32 old_changes = old->u.ctrl.changes;
-
- old->u.ctrl = new->u.ctrl;
- old->u.ctrl.changes |= old_changes;
-}
-
-static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new)
-{
- new->u.ctrl.changes |= old->u.ctrl.changes;
-}
-
int v4l2_event_subscribe(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub, unsigned elems)
+ struct v4l2_event_subscription *sub, unsigned elems,
+ const struct v4l2_subscribed_event_ops *ops)
{
struct v4l2_subscribed_event *sev, *found_ev;
- struct v4l2_ctrl *ctrl = NULL;
unsigned long flags;
unsigned i;
@@ -221,11 +215,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
if (elems < 1)
elems = 1;
- if (sub->type == V4L2_EVENT_CTRL) {
- ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
- if (ctrl == NULL)
- return -EINVAL;
- }
sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
if (!sev)
@@ -236,11 +225,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
sev->id = sub->id;
sev->flags = sub->flags;
sev->fh = fh;
- sev->elems = elems;
- if (ctrl) {
- sev->replace = ctrls_replace;
- sev->merge = ctrls_merge;
- }
+ sev->ops = ops;
spin_lock_irqsave(&fh->vdev->fh_lock, flags);
found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
@@ -248,11 +233,22 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
list_add(&sev->list, &fh->subscribed);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
- if (found_ev)
+ if (found_ev) {
kfree(sev);
- else if (ctrl)
- v4l2_ctrl_add_event(ctrl, sev);
+ return 0; /* Already listening */
+ }
+
+ if (sev->ops && sev->ops->add) {
+ int ret = sev->ops->add(sev, elems);
+ if (ret) {
+ sev->ops = NULL;
+ v4l2_event_unsubscribe(fh, sub);
+ return ret;
+ }
+ }
+
+ /* Mark as ready for use */
+ sev->elems = elems;
return 0;
}
@@ -306,12 +302,9 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
}
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
- if (sev && sev->type == V4L2_EVENT_CTRL) {
- struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
- if (ctrl)
- v4l2_ctrl_del_event(ctrl, sev);
- }
+ if (sev && sev->ops && sev->ops->del)
+ sev->ops->del(sev);
kfree(sev);
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 5b2ec1fd2d0..91be4e871f4 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -55,19 +55,6 @@
memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
-#define have_fmt_ops(foo) ( \
- ops->vidioc_##foo##_fmt_vid_cap || \
- ops->vidioc_##foo##_fmt_vid_out || \
- ops->vidioc_##foo##_fmt_vid_cap_mplane || \
- ops->vidioc_##foo##_fmt_vid_out_mplane || \
- ops->vidioc_##foo##_fmt_vid_overlay || \
- ops->vidioc_##foo##_fmt_vbi_cap || \
- ops->vidioc_##foo##_fmt_vid_out_overlay || \
- ops->vidioc_##foo##_fmt_vbi_out || \
- ops->vidioc_##foo##_fmt_sliced_vbi_cap || \
- ops->vidioc_##foo##_fmt_sliced_vbi_out || \
- ops->vidioc_##foo##_fmt_type_private)
-
struct std_descr {
v4l2_std_id std;
const char *descr;
@@ -195,93 +182,118 @@ static const char *v4l2_memory_names[] = {
/* ------------------------------------------------------------------ */
/* debug help functions */
-static const char *v4l2_ioctls[] = {
- [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP",
- [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED",
- [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT",
- [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT",
- [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT",
- [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS",
- [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF",
- [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF",
- [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF",
- [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY",
- [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF",
- [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF",
- [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON",
- [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF",
- [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM",
- [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM",
- [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD",
- [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD",
- [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD",
- [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT",
- [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL",
- [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL",
- [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER",
- [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER",
- [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO",
- [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO",
- [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL",
- [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU",
- [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT",
- [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT",
- [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT",
- [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT",
- [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT",
- [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT",
- [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT",
- [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR",
- [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR",
- [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY",
- [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY",
- [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP",
- [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP",
- [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP",
- [_IOC_NR(VIDIOC_G_SELECTION)] = "VIDIOC_G_SELECTION",
- [_IOC_NR(VIDIOC_S_SELECTION)] = "VIDIOC_S_SELECTION",
- [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP",
- [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP",
- [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD",
- [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT",
- [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO",
- [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT",
- [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY",
- [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY",
- [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP",
- [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS",
- [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS",
- [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS",
- [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS",
-#if 1
- [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES",
- [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS",
- [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX",
- [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD",
- [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD",
-
- [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD",
- [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD",
- [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER",
- [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER",
-
- [_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT",
- [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK",
+
+struct v4l2_ioctl_info {
+ unsigned int ioctl;
+ u16 flags;
+ const char * const name;
+};
+
+/* This control needs a priority check */
+#define INFO_FL_PRIO (1 << 0)
+/* This control can be valid if the filehandle passes a control handler. */
+#define INFO_FL_CTRL (1 << 1)
+
+#define IOCTL_INFO(_ioctl, _flags) [_IOC_NR(_ioctl)] = { \
+ .ioctl = _ioctl, \
+ .flags = _flags, \
+ .name = #_ioctl, \
+}
+
+static struct v4l2_ioctl_info v4l2_ioctls[] = {
+ IOCTL_INFO(VIDIOC_QUERYCAP, 0),
+ IOCTL_INFO(VIDIOC_ENUM_FMT, 0),
+ IOCTL_INFO(VIDIOC_G_FMT, 0),
+ IOCTL_INFO(VIDIOC_S_FMT, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_REQBUFS, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_QUERYBUF, 0),
+ IOCTL_INFO(VIDIOC_G_FBUF, 0),
+ IOCTL_INFO(VIDIOC_S_FBUF, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_OVERLAY, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_QBUF, 0),
+ IOCTL_INFO(VIDIOC_DQBUF, 0),
+ IOCTL_INFO(VIDIOC_STREAMON, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_STREAMOFF, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_PARM, 0),
+ IOCTL_INFO(VIDIOC_S_PARM, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_STD, 0),
+ IOCTL_INFO(VIDIOC_S_STD, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_ENUMSTD, 0),
+ IOCTL_INFO(VIDIOC_ENUMINPUT, 0),
+ IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_G_TUNER, 0),
+ IOCTL_INFO(VIDIOC_S_TUNER, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_AUDIO, 0),
+ IOCTL_INFO(VIDIOC_S_AUDIO, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_G_INPUT, 0),
+ IOCTL_INFO(VIDIOC_S_INPUT, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_OUTPUT, 0),
+ IOCTL_INFO(VIDIOC_S_OUTPUT, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0),
+ IOCTL_INFO(VIDIOC_G_AUDOUT, 0),
+ IOCTL_INFO(VIDIOC_S_AUDOUT, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_MODULATOR, 0),
+ IOCTL_INFO(VIDIOC_S_MODULATOR, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_FREQUENCY, 0),
+ IOCTL_INFO(VIDIOC_S_FREQUENCY, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_CROPCAP, 0),
+ IOCTL_INFO(VIDIOC_G_CROP, 0),
+ IOCTL_INFO(VIDIOC_S_CROP, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_SELECTION, 0),
+ IOCTL_INFO(VIDIOC_S_SELECTION, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0),
+ IOCTL_INFO(VIDIOC_S_JPEGCOMP, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_QUERYSTD, 0),
+ IOCTL_INFO(VIDIOC_TRY_FMT, 0),
+ IOCTL_INFO(VIDIOC_ENUMAUDIO, 0),
+ IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0),
+ IOCTL_INFO(VIDIOC_G_PRIORITY, 0),
+ IOCTL_INFO(VIDIOC_S_PRIORITY, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0),
+ IOCTL_INFO(VIDIOC_LOG_STATUS, 0),
+ IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_PRIO | INFO_FL_CTRL),
+ IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0),
+ IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0),
+ IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0),
+ IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0),
+ IOCTL_INFO(VIDIOC_ENCODER_CMD, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0),
+ IOCTL_INFO(VIDIOC_DECODER_CMD, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0),
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0),
+ IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0),
#endif
- [_IOC_NR(VIDIOC_ENUM_DV_PRESETS)] = "VIDIOC_ENUM_DV_PRESETS",
- [_IOC_NR(VIDIOC_S_DV_PRESET)] = "VIDIOC_S_DV_PRESET",
- [_IOC_NR(VIDIOC_G_DV_PRESET)] = "VIDIOC_G_DV_PRESET",
- [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET",
- [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS",
- [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS",
- [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT",
- [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT",
- [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT",
- [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS",
- [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF",
+ IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0),
+ IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0),
+ IOCTL_INFO(VIDIOC_S_DV_PRESET, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_DV_PRESET, 0),
+ IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0),
+ IOCTL_INFO(VIDIOC_S_DV_TIMINGS, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0),
+ IOCTL_INFO(VIDIOC_DQEVENT, 0),
+ IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0),
+ IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0),
+ IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO),
+ IOCTL_INFO(VIDIOC_PREPARE_BUF, 0),
+ IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, 0),
+ IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, 0),
+ IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, 0),
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+bool v4l2_is_known_ioctl(unsigned int cmd)
+{
+ if (_IOC_NR(cmd) >= V4L2_IOCTLS)
+ return false;
+ return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
+}
+
/* Common ioctl debug function. This function can be used by
external ioctl messages as well as internal V4L ioctl */
void v4l_printk_ioctl(unsigned int cmd)
@@ -297,7 +309,7 @@ void v4l_printk_ioctl(unsigned int cmd)
type = "v4l2";
break;
}
- printk("%s", v4l2_ioctls[_IOC_NR(cmd)]);
+ printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
return;
default:
type = "unknown";
@@ -359,6 +371,34 @@ static inline void dbgrect(struct video_device *vfd, char *s,
r->width, r->height);
};
+static void dbgtimings(struct video_device *vfd,
+ const struct v4l2_dv_timings *p)
+{
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ dbgarg2("bt-656/1120:interlaced=%d,"
+ " pixelclock=%lld,"
+ " width=%d, height=%d, polarities=%x,"
+ " hfrontporch=%d, hsync=%d,"
+ " hbackporch=%d, vfrontporch=%d,"
+ " vsync=%d, vbackporch=%d,"
+ " il_vfrontporch=%d, il_vsync=%d,"
+ " il_vbackporch=%d, standards=%x, flags=%x\n",
+ p->bt.interlaced, p->bt.pixelclock,
+ p->bt.width, p->bt.height,
+ p->bt.polarities, p->bt.hfrontporch,
+ p->bt.hsync, p->bt.hbackporch,
+ p->bt.vfrontporch, p->bt.vsync,
+ p->bt.vbackporch, p->bt.il_vfrontporch,
+ p->bt.il_vsync, p->bt.il_vbackporch,
+ p->bt.standards, p->bt.flags);
+ break;
+ default:
+ dbgarg2("Unknown type %d!\n", p->type);
+ break;
+ }
+}
+
static inline void v4l_print_pix_fmt(struct video_device *vfd,
struct v4l2_pix_format *fmt)
{
@@ -504,7 +544,6 @@ static long __video_do_ioctl(struct file *file,
void *fh = file->private_data;
struct v4l2_fh *vfh = NULL;
int use_fh_prio = 0;
- long ret_prio = 0;
long ret = -ENOTTY;
if (ops == NULL) {
@@ -513,19 +552,30 @@ static long __video_do_ioctl(struct file *file,
return ret;
}
- if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
- !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {
- v4l_print_ioctl(vfd->name, cmd);
- printk(KERN_CONT "\n");
- }
-
if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
vfh = file->private_data;
use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
}
- if (use_fh_prio)
- ret_prio = v4l2_prio_check(vfd->prio, vfh->prio);
+ if (v4l2_is_known_ioctl(cmd)) {
+ struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)];
+
+ if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
+ !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
+ return -ENOTTY;
+
+ if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
+ ret = v4l2_prio_check(vfd->prio, vfh->prio);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if ((vfd->debug & V4L2_DEBUG_IOCTL) &&
+ !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) {
+ v4l_print_ioctl(vfd->name, cmd);
+ printk(KERN_CONT "\n");
+ }
switch (cmd) {
@@ -534,9 +584,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_capability *cap = (struct v4l2_capability *)arg;
- if (!ops->vidioc_querycap)
- break;
-
cap->version = LINUX_VERSION_CODE;
ret = ops->vidioc_querycap(file, fh, cap);
if (!ret)
@@ -570,14 +617,11 @@ static long __video_do_ioctl(struct file *file,
{
enum v4l2_priority *p = arg;
- if (!ops->vidioc_s_priority && !use_fh_prio)
- break;
dbgarg(cmd, "setting priority to %d\n", *p);
if (ops->vidioc_s_priority)
ret = ops->vidioc_s_priority(file, fh, *p);
else
- ret = ret_prio ? ret_prio :
- v4l2_prio_change(&vfd->v4l2_dev->prio,
+ ret = v4l2_prio_change(&vfd->v4l2_dev->prio,
&vfh->prio, *p);
break;
}
@@ -587,6 +631,7 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_fmtdesc *f = arg;
+ ret = -EINVAL;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (likely(ops->vidioc_enum_fmt_vid_cap))
@@ -619,7 +664,7 @@ static long __video_do_ioctl(struct file *file,
default:
break;
}
- if (likely (!ret))
+ if (likely(!ret))
dbgarg(cmd, "index=%d, type=%d, flags=%d, "
"pixelformat=%c%c%c%c, description='%s'\n",
f->index, f->type, f->flags,
@@ -628,14 +673,6 @@ static long __video_do_ioctl(struct file *file,
(f->pixelformat >> 16) & 0xff,
(f->pixelformat >> 24) & 0xff,
f->description);
- else if (ret == -ENOTTY &&
- (ops->vidioc_enum_fmt_vid_cap ||
- ops->vidioc_enum_fmt_vid_out ||
- ops->vidioc_enum_fmt_vid_cap_mplane ||
- ops->vidioc_enum_fmt_vid_out_mplane ||
- ops->vidioc_enum_fmt_vid_overlay ||
- ops->vidioc_enum_fmt_type_private))
- ret = -EINVAL;
break;
}
case VIDIOC_G_FMT:
@@ -645,6 +682,7 @@ static long __video_do_ioctl(struct file *file,
/* FIXME: Should be one dump per type */
dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
+ ret = -EINVAL;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (ops->vidioc_g_fmt_vid_cap)
@@ -706,21 +744,12 @@ static long __video_do_ioctl(struct file *file,
fh, f);
break;
}
- if (unlikely(ret == -ENOTTY && have_fmt_ops(g)))
- ret = -EINVAL;
-
break;
}
case VIDIOC_S_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)arg;
- if (!have_fmt_ops(s))
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
ret = -EINVAL;
/* FIXME: Should be one dump per type */
@@ -804,6 +833,7 @@ static long __video_do_ioctl(struct file *file,
/* FIXME: Should be one dump per type */
dbgarg(cmd, "type=%s\n", prt_names(f->type,
v4l2_type_names));
+ ret = -EINVAL;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
CLEAR_AFTER_FIELD(f, fmt.pix);
@@ -876,8 +906,6 @@ static long __video_do_ioctl(struct file *file,
fh, f);
break;
}
- if (unlikely(ret == -ENOTTY && have_fmt_ops(try)))
- ret = -EINVAL;
break;
}
/* FIXME: Those buf reqs could be handled here,
@@ -888,12 +916,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_requestbuffers *p = arg;
- if (!ops->vidioc_reqbufs)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -912,8 +934,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_buffer *p = arg;
- if (!ops->vidioc_querybuf)
- break;
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -927,8 +947,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_buffer *p = arg;
- if (!ops->vidioc_qbuf)
- break;
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -942,8 +960,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_buffer *p = arg;
- if (!ops->vidioc_dqbuf)
- break;
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -957,12 +973,6 @@ static long __video_do_ioctl(struct file *file,
{
int *i = arg;
- if (!ops->vidioc_overlay)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_overlay(file, fh, *i);
break;
@@ -971,8 +981,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_framebuffer *p = arg;
- if (!ops->vidioc_g_fbuf)
- break;
ret = ops->vidioc_g_fbuf(file, fh, arg);
if (!ret) {
dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
@@ -986,12 +994,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_framebuffer *p = arg;
- if (!ops->vidioc_s_fbuf)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n",
p->capability, p->flags, (unsigned long)p->base);
v4l_print_pix_fmt(vfd, &p->fmt);
@@ -1002,12 +1004,6 @@ static long __video_do_ioctl(struct file *file,
{
enum v4l2_buf_type i = *(int *)arg;
- if (!ops->vidioc_streamon)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
ret = ops->vidioc_streamon(file, fh, i);
break;
@@ -1016,12 +1012,6 @@ static long __video_do_ioctl(struct file *file,
{
enum v4l2_buf_type i = *(int *)arg;
- if (!ops->vidioc_streamoff)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names));
ret = ops->vidioc_streamoff(file, fh, i);
break;
@@ -1091,13 +1081,6 @@ static long __video_do_ioctl(struct file *file,
dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id);
- if (!ops->vidioc_s_std)
- break;
-
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
ret = -EINVAL;
norm = (*id) & vfd->tvnorms;
if (vfd->tvnorms && !norm) /* Check if std is supported */
@@ -1115,8 +1098,6 @@ static long __video_do_ioctl(struct file *file,
{
v4l2_std_id *p = arg;
- if (!ops->vidioc_querystd)
- break;
/*
* If nothing detected, it should return all supported
* Drivers just need to mask the std argument, in order
@@ -1150,9 +1131,6 @@ static long __video_do_ioctl(struct file *file,
if (ops->vidioc_s_dv_timings)
p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
- if (!ops->vidioc_enum_input)
- break;
-
ret = ops->vidioc_enum_input(file, fh, p);
if (!ret)
dbgarg(cmd, "index=%d, name=%s, type=%d, "
@@ -1168,8 +1146,6 @@ static long __video_do_ioctl(struct file *file,
{
unsigned int *i = arg;
- if (!ops->vidioc_g_input)
- break;
ret = ops->vidioc_g_input(file, fh, i);
if (!ret)
dbgarg(cmd, "value=%d\n", *i);
@@ -1179,12 +1155,6 @@ static long __video_do_ioctl(struct file *file,
{
unsigned int *i = arg;
- if (!ops->vidioc_s_input)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_s_input(file, fh, *i);
break;
@@ -1195,9 +1165,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_output *p = arg;
- if (!ops->vidioc_enum_output)
- break;
-
/*
* We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
* CAP_STD here based on ioctl handler provided by the
@@ -1224,8 +1191,6 @@ static long __video_do_ioctl(struct file *file,
{
unsigned int *i = arg;
- if (!ops->vidioc_g_output)
- break;
ret = ops->vidioc_g_output(file, fh, i);
if (!ret)
dbgarg(cmd, "value=%d\n", *i);
@@ -1235,12 +1200,6 @@ static long __video_do_ioctl(struct file *file,
{
unsigned int *i = arg;
- if (!ops->vidioc_s_output)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "value=%d\n", *i);
ret = ops->vidioc_s_output(file, fh, *i);
break;
@@ -1310,10 +1269,6 @@ static long __video_do_ioctl(struct file *file,
if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
@@ -1369,10 +1324,6 @@ static long __video_do_ioctl(struct file *file,
if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
!ops->vidioc_s_ext_ctrls)
break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
v4l_print_ext_ctrls(cmd, vfd, p, 1);
if (vfh && vfh->ctrl_handler)
ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
@@ -1428,8 +1379,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_audio *p = arg;
- if (!ops->vidioc_enumaudio)
- break;
ret = ops->vidioc_enumaudio(file, fh, p);
if (!ret)
dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
@@ -1443,9 +1392,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_audio *p = arg;
- if (!ops->vidioc_g_audio)
- break;
-
ret = ops->vidioc_g_audio(file, fh, p);
if (!ret)
dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
@@ -1459,12 +1405,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_audio *p = arg;
- if (!ops->vidioc_s_audio)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
"mode=0x%x\n", p->index, p->name,
p->capability, p->mode);
@@ -1475,8 +1415,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_audioout *p = arg;
- if (!ops->vidioc_enumaudout)
- break;
dbgarg(cmd, "Enum for index=%d\n", p->index);
ret = ops->vidioc_enumaudout(file, fh, p);
if (!ret)
@@ -1489,9 +1427,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_audioout *p = arg;
- if (!ops->vidioc_g_audout)
- break;
-
ret = ops->vidioc_g_audout(file, fh, p);
if (!ret)
dbgarg2("index=%d, name=%s, capability=%d, "
@@ -1503,12 +1438,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_audioout *p = arg;
- if (!ops->vidioc_s_audout)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "index=%d, name=%s, capability=%d, "
"mode=%d\n", p->index, p->name,
p->capability, p->mode);
@@ -1520,8 +1449,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_modulator *p = arg;
- if (!ops->vidioc_g_modulator)
- break;
ret = ops->vidioc_g_modulator(file, fh, p);
if (!ret)
dbgarg(cmd, "index=%d, name=%s, "
@@ -1536,12 +1463,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_modulator *p = arg;
- if (!ops->vidioc_s_modulator)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "index=%d, name=%s, capability=%d, "
"rangelow=%d, rangehigh=%d, txsubchans=%d\n",
p->index, p->name, p->capability, p->rangelow,
@@ -1553,9 +1474,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_crop *p = arg;
- if (!ops->vidioc_g_crop && !ops->vidioc_g_selection)
- break;
-
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
if (ops->vidioc_g_crop) {
@@ -1587,13 +1505,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_crop *p = arg;
- if (!ops->vidioc_s_crop && !ops->vidioc_s_selection)
- break;
-
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
dbgrect(vfd, "", &p->c);
@@ -1620,9 +1531,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_selection *p = arg;
- if (!ops->vidioc_g_selection)
- break;
-
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
ret = ops->vidioc_g_selection(file, fh, p);
@@ -1634,13 +1542,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_selection *p = arg;
- if (!ops->vidioc_s_selection)
- break;
-
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
dbgrect(vfd, "", &p->r);
@@ -1653,9 +1554,6 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_cropcap *p = arg;
/*FIXME: Should also show v4l2_fract pixelaspect */
- if (!ops->vidioc_cropcap && !ops->vidioc_g_selection)
- break;
-
dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
if (ops->vidioc_cropcap) {
ret = ops->vidioc_cropcap(file, fh, p);
@@ -1699,9 +1597,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_jpegcompression *p = arg;
- if (!ops->vidioc_g_jpegcomp)
- break;
-
ret = ops->vidioc_g_jpegcomp(file, fh, p);
if (!ret)
dbgarg(cmd, "quality=%d, APPn=%d, "
@@ -1715,12 +1610,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_jpegcompression *p = arg;
- if (!ops->vidioc_g_jpegcomp)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, "
"COM_len=%d, jpeg_markers=%d\n",
p->quality, p->APPn, p->APP_len,
@@ -1732,8 +1621,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_enc_idx *p = arg;
- if (!ops->vidioc_g_enc_index)
- break;
ret = ops->vidioc_g_enc_index(file, fh, p);
if (!ret)
dbgarg(cmd, "entries=%d, entries_cap=%d\n",
@@ -1744,12 +1631,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_encoder_cmd *p = arg;
- if (!ops->vidioc_encoder_cmd)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
ret = ops->vidioc_encoder_cmd(file, fh, p);
if (!ret)
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
@@ -1759,8 +1640,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_encoder_cmd *p = arg;
- if (!ops->vidioc_try_encoder_cmd)
- break;
ret = ops->vidioc_try_encoder_cmd(file, fh, p);
if (!ret)
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
@@ -1770,12 +1649,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_decoder_cmd *p = arg;
- if (!ops->vidioc_decoder_cmd)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
ret = ops->vidioc_decoder_cmd(file, fh, p);
if (!ret)
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
@@ -1785,8 +1658,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_decoder_cmd *p = arg;
- if (!ops->vidioc_try_decoder_cmd)
- break;
ret = ops->vidioc_try_decoder_cmd(file, fh, p);
if (!ret)
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
@@ -1796,8 +1667,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_streamparm *p = arg;
- if (!ops->vidioc_g_parm && !vfd->current_norm)
- break;
if (ops->vidioc_g_parm) {
ret = check_fmt(ops, p->type);
if (ret)
@@ -1825,12 +1694,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_streamparm *p = arg;
- if (!ops->vidioc_s_parm)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
ret = check_fmt(ops, p->type);
if (ret)
break;
@@ -1843,9 +1706,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_tuner *p = arg;
- if (!ops->vidioc_g_tuner)
- break;
-
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
ret = ops->vidioc_g_tuner(file, fh, p);
@@ -1864,12 +1724,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_tuner *p = arg;
- if (!ops->vidioc_s_tuner)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
dbgarg(cmd, "index=%d, name=%s, type=%d, "
@@ -1887,9 +1741,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_frequency *p = arg;
- if (!ops->vidioc_g_frequency)
- break;
-
p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
ret = ops->vidioc_g_frequency(file, fh, p);
@@ -1903,12 +1754,6 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_frequency *p = arg;
enum v4l2_tuner_type type;
- if (!ops->vidioc_s_frequency)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
@@ -1923,9 +1768,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_sliced_vbi_cap *p = arg;
- if (!ops->vidioc_g_sliced_vbi_cap)
- break;
-
/* Clear up to type, everything after type is zerod already */
memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
@@ -1937,8 +1779,6 @@ static long __video_do_ioctl(struct file *file,
}
case VIDIOC_LOG_STATUS:
{
- if (!ops->vidioc_log_status)
- break;
if (vfd->v4l2_dev)
pr_info("%s: ================= START STATUS =================\n",
vfd->v4l2_dev->name);
@@ -1948,38 +1788,34 @@ static long __video_do_ioctl(struct file *file,
vfd->v4l2_dev->name);
break;
}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
case VIDIOC_DBG_G_REGISTER:
{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
struct v4l2_dbg_register *p = arg;
- if (ops->vidioc_g_register) {
- if (!capable(CAP_SYS_ADMIN))
- ret = -EPERM;
- else
- ret = ops->vidioc_g_register(file, fh, p);
- }
+ if (!capable(CAP_SYS_ADMIN))
+ ret = -EPERM;
+ else
+ ret = ops->vidioc_g_register(file, fh, p);
+#endif
break;
}
case VIDIOC_DBG_S_REGISTER:
{
+#ifdef CONFIG_VIDEO_ADV_DEBUG
struct v4l2_dbg_register *p = arg;
- if (ops->vidioc_s_register) {
- if (!capable(CAP_SYS_ADMIN))
- ret = -EPERM;
- else
- ret = ops->vidioc_s_register(file, fh, p);
- }
+ if (!capable(CAP_SYS_ADMIN))
+ ret = -EPERM;
+ else
+ ret = ops->vidioc_s_register(file, fh, p);
+#endif
break;
}
-#endif
case VIDIOC_DBG_G_CHIP_IDENT:
{
struct v4l2_dbg_chip_ident *p = arg;
- if (!ops->vidioc_g_chip_ident)
- break;
p->ident = V4L2_IDENT_NONE;
p->revision = 0;
ret = ops->vidioc_g_chip_ident(file, fh, p);
@@ -1992,12 +1828,6 @@ static long __video_do_ioctl(struct file *file,
struct v4l2_hw_freq_seek *p = arg;
enum v4l2_tuner_type type;
- if (!ops->vidioc_s_hw_freq_seek)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
dbgarg(cmd,
@@ -2013,9 +1843,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_frmsizeenum *p = arg;
- if (!ops->vidioc_enum_framesizes)
- break;
-
ret = ops->vidioc_enum_framesizes(file, fh, p);
dbgarg(cmd,
"index=%d, pixelformat=%c%c%c%c, type=%d ",
@@ -2049,9 +1876,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_frmivalenum *p = arg;
- if (!ops->vidioc_enum_frameintervals)
- break;
-
ret = ops->vidioc_enum_frameintervals(file, fh, p);
dbgarg(cmd,
"index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
@@ -2084,9 +1908,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_dv_enum_preset *p = arg;
- if (!ops->vidioc_enum_dv_presets)
- break;
-
ret = ops->vidioc_enum_dv_presets(file, fh, p);
if (!ret)
dbgarg(cmd,
@@ -2100,13 +1921,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_dv_preset *p = arg;
- if (!ops->vidioc_s_dv_preset)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
-
dbgarg(cmd, "preset=%d\n", p->preset);
ret = ops->vidioc_s_dv_preset(file, fh, p);
break;
@@ -2115,9 +1929,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_dv_preset *p = arg;
- if (!ops->vidioc_g_dv_preset)
- break;
-
ret = ops->vidioc_g_dv_preset(file, fh, p);
if (!ret)
dbgarg(cmd, "preset=%d\n", p->preset);
@@ -2127,9 +1938,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_dv_preset *p = arg;
- if (!ops->vidioc_query_dv_preset)
- break;
-
ret = ops->vidioc_query_dv_preset(file, fh, p);
if (!ret)
dbgarg(cmd, "preset=%d\n", p->preset);
@@ -2139,32 +1947,13 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_dv_timings *p = arg;
- if (!ops->vidioc_s_dv_timings)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
-
+ dbgtimings(vfd, p);
switch (p->type) {
case V4L2_DV_BT_656_1120:
- dbgarg2("bt-656/1120:interlaced=%d, pixelclock=%lld,"
- " width=%d, height=%d, polarities=%x,"
- " hfrontporch=%d, hsync=%d, hbackporch=%d,"
- " vfrontporch=%d, vsync=%d, vbackporch=%d,"
- " il_vfrontporch=%d, il_vsync=%d,"
- " il_vbackporch=%d\n",
- p->bt.interlaced, p->bt.pixelclock,
- p->bt.width, p->bt.height, p->bt.polarities,
- p->bt.hfrontporch, p->bt.hsync,
- p->bt.hbackporch, p->bt.vfrontporch,
- p->bt.vsync, p->bt.vbackporch,
- p->bt.il_vfrontporch, p->bt.il_vsync,
- p->bt.il_vbackporch);
ret = ops->vidioc_s_dv_timings(file, fh, p);
break;
default:
- dbgarg2("Unknown type %d!\n", p->type);
+ ret = -EINVAL;
break;
}
break;
@@ -2173,43 +1962,68 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_dv_timings *p = arg;
- if (!ops->vidioc_g_dv_timings)
+ ret = ops->vidioc_g_dv_timings(file, fh, p);
+ if (!ret)
+ dbgtimings(vfd, p);
+ break;
+ }
+ case VIDIOC_ENUM_DV_TIMINGS:
+ {
+ struct v4l2_enum_dv_timings *p = arg;
+
+ if (!ops->vidioc_enum_dv_timings)
break;
- ret = ops->vidioc_g_dv_timings(file, fh, p);
+ ret = ops->vidioc_enum_dv_timings(file, fh, p);
if (!ret) {
- switch (p->type) {
- case V4L2_DV_BT_656_1120:
- dbgarg2("bt-656/1120:interlaced=%d,"
- " pixelclock=%lld,"
- " width=%d, height=%d, polarities=%x,"
- " hfrontporch=%d, hsync=%d,"
- " hbackporch=%d, vfrontporch=%d,"
- " vsync=%d, vbackporch=%d,"
- " il_vfrontporch=%d, il_vsync=%d,"
- " il_vbackporch=%d\n",
- p->bt.interlaced, p->bt.pixelclock,
- p->bt.width, p->bt.height,
- p->bt.polarities, p->bt.hfrontporch,
- p->bt.hsync, p->bt.hbackporch,
- p->bt.vfrontporch, p->bt.vsync,
- p->bt.vbackporch, p->bt.il_vfrontporch,
- p->bt.il_vsync, p->bt.il_vbackporch);
- break;
- default:
- dbgarg2("Unknown type %d!\n", p->type);
- break;
- }
+ dbgarg(cmd, "index=%d: ", p->index);
+ dbgtimings(vfd, &p->timings);
}
break;
}
- case VIDIOC_DQEVENT:
+ case VIDIOC_QUERY_DV_TIMINGS:
{
- struct v4l2_event *ev = arg;
+ struct v4l2_dv_timings *p = arg;
+
+ if (!ops->vidioc_query_dv_timings)
+ break;
- if (!ops->vidioc_subscribe_event)
+ ret = ops->vidioc_query_dv_timings(file, fh, p);
+ if (!ret)
+ dbgtimings(vfd, p);
+ break;
+ }
+ case VIDIOC_DV_TIMINGS_CAP:
+ {
+ struct v4l2_dv_timings_cap *p = arg;
+
+ if (!ops->vidioc_dv_timings_cap)
break;
+ ret = ops->vidioc_dv_timings_cap(file, fh, p);
+ if (ret)
+ break;
+ switch (p->type) {
+ case V4L2_DV_BT_656_1120:
+ dbgarg(cmd,
+ "type=%d, width=%u-%u, height=%u-%u, "
+ "pixelclock=%llu-%llu, standards=%x, capabilities=%x ",
+ p->type,
+ p->bt.min_width, p->bt.max_width,
+ p->bt.min_height, p->bt.max_height,
+ p->bt.min_pixelclock, p->bt.max_pixelclock,
+ p->bt.standards, p->bt.capabilities);
+ break;
+ default:
+ dbgarg(cmd, "unknown type ");
+ break;
+ }
+ break;
+ }
+ case VIDIOC_DQEVENT:
+ {
+ struct v4l2_event *ev = arg;
+
ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK);
if (ret < 0) {
dbgarg(cmd, "no pending events?");
@@ -2226,9 +2040,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_event_subscription *sub = arg;
- if (!ops->vidioc_subscribe_event)
- break;
-
ret = ops->vidioc_subscribe_event(fh, sub);
if (ret < 0) {
dbgarg(cmd, "failed, ret=%ld", ret);
@@ -2241,9 +2052,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_event_subscription *sub = arg;
- if (!ops->vidioc_unsubscribe_event)
- break;
-
ret = ops->vidioc_unsubscribe_event(fh, sub);
if (ret < 0) {
dbgarg(cmd, "failed, ret=%ld", ret);
@@ -2256,12 +2064,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_create_buffers *create = arg;
- if (!ops->vidioc_create_bufs)
- break;
- if (ret_prio) {
- ret = ret_prio;
- break;
- }
ret = check_fmt(ops, create->format.type);
if (ret)
break;
@@ -2275,8 +2077,6 @@ static long __video_do_ioctl(struct file *file,
{
struct v4l2_buffer *b = arg;
- if (!ops->vidioc_prepare_buf)
- break;
ret = check_fmt(ops, b->type);
if (ret)
break;
@@ -2289,7 +2089,9 @@ static long __video_do_ioctl(struct file *file,
default:
if (!ops->vidioc_default)
break;
- ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg);
+ ret = ops->vidioc_default(file, fh, use_fh_prio ?
+ v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
+ cmd, arg);
break;
} /* switch */
@@ -2463,7 +2265,9 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
err = -EFAULT;
goto out_array_args;
}
- if (err < 0)
+ /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid
+ results that must be returned. */
+ if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS)
goto out;
out_array_args:
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
index 6fe88e965a8..db6e859b93d 100644
--- a/drivers/media/video/v4l2-subdev.c
+++ b/drivers/media/video/v4l2-subdev.c
@@ -35,14 +35,9 @@
static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
{
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
- /* Allocate try format and crop in the same memory block */
- fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop))
- * sd->entity.num_pads, GFP_KERNEL);
- if (fh->try_fmt == NULL)
+ fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL);
+ if (fh->pad == NULL)
return -ENOMEM;
-
- fh->try_crop = (struct v4l2_rect *)
- (fh->try_fmt + sd->entity.num_pads);
#endif
return 0;
}
@@ -50,9 +45,8 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd)
static void subdev_fh_free(struct v4l2_subdev_fh *fh)
{
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
- kfree(fh->try_fmt);
- fh->try_fmt = NULL;
- fh->try_crop = NULL;
+ kfree(fh->pad);
+ fh->pad = NULL;
#endif
}
@@ -234,6 +228,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_G_CROP: {
struct v4l2_subdev_crop *crop = arg;
+ struct v4l2_subdev_selection sel;
+ int rval;
if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
@@ -242,11 +238,27 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (crop->pad >= sd->entity.num_pads)
return -EINVAL;
- return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
+ rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop);
+ if (rval != -ENOIOCTLCMD)
+ return rval;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.which = crop->which;
+ sel.pad = crop->pad;
+ sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL;
+
+ rval = v4l2_subdev_call(
+ sd, pad, get_selection, subdev_fh, &sel);
+
+ crop->rect = sel.r;
+
+ return rval;
}
case VIDIOC_SUBDEV_S_CROP: {
struct v4l2_subdev_crop *crop = arg;
+ struct v4l2_subdev_selection sel;
+ int rval;
if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
@@ -255,7 +267,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (crop->pad >= sd->entity.num_pads)
return -EINVAL;
- return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
+ rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop);
+ if (rval != -ENOIOCTLCMD)
+ return rval;
+
+ memset(&sel, 0, sizeof(sel));
+ sel.which = crop->which;
+ sel.pad = crop->pad;
+ sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL;
+ sel.r = crop->rect;
+
+ rval = v4l2_subdev_call(
+ sd, pad, set_selection, subdev_fh, &sel);
+
+ crop->rect = sel.r;
+
+ return rval;
}
case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
@@ -293,6 +320,34 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh,
fie);
}
+
+ case VIDIOC_SUBDEV_G_SELECTION: {
+ struct v4l2_subdev_selection *sel = arg;
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
+ sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (sel->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(
+ sd, pad, get_selection, subdev_fh, sel);
+ }
+
+ case VIDIOC_SUBDEV_S_SELECTION: {
+ struct v4l2_subdev_selection *sel = arg;
+
+ if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
+ sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
+ if (sel->pad >= sd->entity.num_pads)
+ return -EINVAL;
+
+ return v4l2_subdev_call(
+ sd, pad, set_selection, subdev_fh, sel);
+ }
#endif
default:
return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
@@ -332,6 +387,70 @@ const struct v4l2_file_operations v4l2_subdev_fops = {
.poll = subdev_poll,
};
+#ifdef CONFIG_MEDIA_CONTROLLER
+int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ if (source_fmt->format.width != sink_fmt->format.width
+ || source_fmt->format.height != sink_fmt->format.height
+ || source_fmt->format.code != sink_fmt->format.code)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default);
+
+static int
+v4l2_subdev_link_validate_get_format(struct media_pad *pad,
+ struct v4l2_subdev_format *fmt)
+{
+ switch (media_entity_type(pad->entity)) {
+ case MEDIA_ENT_T_V4L2_SUBDEV:
+ fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt->pad = pad->index;
+ return v4l2_subdev_call(media_entity_to_v4l2_subdev(
+ pad->entity),
+ pad, get_fmt, NULL, fmt);
+ default:
+ WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n",
+ media_entity_type(pad->entity), pad->entity->name);
+ /* Fall through */
+ case MEDIA_ENT_T_DEVNODE_V4L:
+ return -EINVAL;
+ }
+}
+
+int v4l2_subdev_link_validate(struct media_link *link)
+{
+ struct v4l2_subdev *sink;
+ struct v4l2_subdev_format sink_fmt, source_fmt;
+ int rval;
+
+ rval = v4l2_subdev_link_validate_get_format(
+ link->source, &source_fmt);
+ if (rval < 0)
+ return 0;
+
+ rval = v4l2_subdev_link_validate_get_format(
+ link->sink, &sink_fmt);
+ if (rval < 0)
+ return 0;
+
+ sink = media_entity_to_v4l2_subdev(link->sink->entity);
+
+ rval = v4l2_subdev_call(sink, pad, link_validate, link,
+ &source_fmt, &sink_fmt);
+ if (rval != -ENOIOCTLCMD)
+ return rval;
+
+ return v4l2_subdev_link_validate_default(
+ sink, link, &source_fmt, &sink_fmt);
+}
+EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate);
+#endif /* CONFIG_MEDIA_CONTROLLER */
+
void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops)
{
INIT_LIST_HEAD(&sd->list);
diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c
index 20f7237b824..308e150a39b 100644
--- a/drivers/media/video/via-camera.c
+++ b/drivers/media/video/via-camera.c
@@ -18,6 +18,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-chip-ident.h>
+#include <media/ov7670.h>
#include <media/videobuf-dma-sg.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -1347,11 +1348,21 @@ static __devinit bool viacam_serial_is_enabled(void)
return false;
}
+static struct ov7670_config sensor_cfg = {
+ /* The XO-1.5 (only known user) clocks the camera at 90MHz. */
+ .clock_speed = 90,
+};
+
static __devinit int viacam_probe(struct platform_device *pdev)
{
int ret;
struct i2c_adapter *sensor_adapter;
struct viafb_dev *viadev = pdev->dev.platform_data;
+ struct i2c_board_info ov7670_info = {
+ .type = "ov7670",
+ .addr = 0x42 >> 1,
+ .platform_data = &sensor_cfg,
+ };
/*
* Note that there are actually two capture channels on
@@ -1433,8 +1444,8 @@ static __devinit int viacam_probe(struct platform_device *pdev)
* is OLPC-specific. 0x42 assumption is ov7670-specific.
*/
sensor_adapter = viafb_find_i2c_adapter(VIA_PORT_31);
- cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, sensor_adapter,
- "ov7670", 0x42 >> 1, NULL);
+ cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, sensor_adapter,
+ &ov7670_info, NULL);
if (cam->sensor == NULL) {
dev_err(&pdev->dev, "Unable to find the sensor!\n");
ret = -ENODEV;
diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c
index de4fa4eb884..ffdf59cfe40 100644
--- a/drivers/media/video/videobuf-core.c
+++ b/drivers/media/video/videobuf-core.c
@@ -1129,6 +1129,7 @@ unsigned int videobuf_poll_stream(struct file *file,
struct videobuf_queue *q,
poll_table *wait)
{
+ unsigned long req_events = poll_requested_events(wait);
struct videobuf_buffer *buf = NULL;
unsigned int rc = 0;
@@ -1137,7 +1138,7 @@ unsigned int videobuf_poll_stream(struct file *file,
if (!list_empty(&q->stream))
buf = list_entry(q->stream.next,
struct videobuf_buffer, stream);
- } else {
+ } else if (req_events & (POLLIN | POLLRDNORM)) {
if (!q->reading)
__videobuf_read_start(q);
if (!q->reading) {
diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
index c9691115f2d..b6b5cc1a43c 100644
--- a/drivers/media/video/videobuf-dma-contig.c
+++ b/drivers/media/video/videobuf-dma-contig.c
@@ -27,6 +27,7 @@ struct videobuf_dma_contig_memory {
u32 magic;
void *vaddr;
dma_addr_t dma_handle;
+ bool cached;
unsigned long size;
};
@@ -37,8 +38,58 @@ struct videobuf_dma_contig_memory {
BUG(); \
}
-static void
-videobuf_vm_open(struct vm_area_struct *vma)
+static int __videobuf_dc_alloc(struct device *dev,
+ struct videobuf_dma_contig_memory *mem,
+ unsigned long size, unsigned long flags)
+{
+ mem->size = size;
+ if (mem->cached) {
+ mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA);
+ if (mem->vaddr) {
+ int err;
+
+ mem->dma_handle = dma_map_single(dev, mem->vaddr,
+ mem->size,
+ DMA_FROM_DEVICE);
+ err = dma_mapping_error(dev, mem->dma_handle);
+ if (err) {
+ dev_err(dev, "dma_map_single failed\n");
+
+ free_pages_exact(mem->vaddr, mem->size);
+ mem->vaddr = 0;
+ return err;
+ }
+ }
+ } else
+ mem->vaddr = dma_alloc_coherent(dev, mem->size,
+ &mem->dma_handle, flags);
+
+ if (!mem->vaddr) {
+ dev_err(dev, "memory alloc size %ld failed\n", mem->size);
+ return -ENOMEM;
+ }
+
+ dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size);
+
+ return 0;
+}
+
+static void __videobuf_dc_free(struct device *dev,
+ struct videobuf_dma_contig_memory *mem)
+{
+ if (mem->cached) {
+ if (!mem->vaddr)
+ return;
+ dma_unmap_single(dev, mem->dma_handle, mem->size,
+ DMA_FROM_DEVICE);
+ free_pages_exact(mem->vaddr, mem->size);
+ } else
+ dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle);
+
+ mem->vaddr = NULL;
+}
+
+static void videobuf_vm_open(struct vm_area_struct *vma)
{
struct videobuf_mapping *map = vma->vm_private_data;
@@ -91,12 +142,11 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
dev_dbg(q->dev, "buf[%d] freeing %p\n",
i, mem->vaddr);
- dma_free_coherent(q->dev, mem->size,
- mem->vaddr, mem->dma_handle);
+ __videobuf_dc_free(q->dev, mem);
mem->vaddr = NULL;
}
- q->bufs[i]->map = NULL;
+ q->bufs[i]->map = NULL;
q->bufs[i]->baddr = 0;
}
@@ -107,8 +157,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma)
}
static const struct vm_operations_struct videobuf_vm_ops = {
- .open = videobuf_vm_open,
- .close = videobuf_vm_close,
+ .open = videobuf_vm_open,
+ .close = videobuf_vm_close,
};
/**
@@ -178,26 +228,38 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem,
pages_done++;
}
- out_up:
+out_up:
up_read(&current->mm->mmap_sem);
return ret;
}
-static struct videobuf_buffer *__videobuf_alloc_vb(size_t size)
+static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached)
{
struct videobuf_dma_contig_memory *mem;
struct videobuf_buffer *vb;
vb = kzalloc(size + sizeof(*mem), GFP_KERNEL);
if (vb) {
- mem = vb->priv = ((char *)vb) + size;
+ vb->priv = ((char *)vb) + size;
+ mem = vb->priv;
mem->magic = MAGIC_DC_MEM;
+ mem->cached = cached;
}
return vb;
}
+static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size)
+{
+ return __videobuf_alloc_vb(size, false);
+}
+
+static struct videobuf_buffer *__videobuf_alloc_cached(size_t size)
+{
+ return __videobuf_alloc_vb(size, true);
+}
+
static void *__videobuf_to_vaddr(struct videobuf_buffer *buf)
{
struct videobuf_dma_contig_memory *mem = buf->priv;
@@ -235,28 +297,32 @@ static int __videobuf_iolock(struct videobuf_queue *q,
return videobuf_dma_contig_user_get(mem, vb);
/* allocate memory for the read() method */
- mem->size = PAGE_ALIGN(vb->size);
- mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
- &mem->dma_handle, GFP_KERNEL);
- if (!mem->vaddr) {
- dev_err(q->dev, "dma_alloc_coherent %ld failed\n",
- mem->size);
+ if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size),
+ GFP_KERNEL))
return -ENOMEM;
- }
-
- dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n",
- mem->vaddr, mem->size);
break;
case V4L2_MEMORY_OVERLAY:
default:
- dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n",
- __func__);
+ dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__);
return -EINVAL;
}
return 0;
}
+static int __videobuf_sync(struct videobuf_queue *q,
+ struct videobuf_buffer *buf)
+{
+ struct videobuf_dma_contig_memory *mem = buf->priv;
+ BUG_ON(!mem);
+ MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
+
+ dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size,
+ DMA_FROM_DEVICE);
+
+ return 0;
+}
+
static int __videobuf_mmap_mapper(struct videobuf_queue *q,
struct videobuf_buffer *buf,
struct vm_area_struct *vma)
@@ -265,6 +331,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
struct videobuf_mapping *map;
int retval;
unsigned long size;
+ unsigned long pos, start = vma->vm_start;
+ struct page *page;
dev_dbg(q->dev, "%s\n", __func__);
@@ -282,41 +350,50 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
BUG_ON(!mem);
MAGIC_CHECK(mem->magic, MAGIC_DC_MEM);
- mem->size = PAGE_ALIGN(buf->bsize);
- mem->vaddr = dma_alloc_coherent(q->dev, mem->size,
- &mem->dma_handle, GFP_KERNEL);
- if (!mem->vaddr) {
- dev_err(q->dev, "dma_alloc_coherent size %ld failed\n",
- mem->size);
+ if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize),
+ GFP_KERNEL | __GFP_COMP))
goto error;
- }
- dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n",
- mem->vaddr, mem->size);
/* Try to remap memory */
size = vma->vm_end - vma->vm_start;
size = (size < mem->size) ? size : mem->size;
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- retval = remap_pfn_range(vma, vma->vm_start,
- mem->dma_handle >> PAGE_SHIFT,
- size, vma->vm_page_prot);
- if (retval) {
- dev_err(q->dev, "mmap: remap failed with error %d. ", retval);
- dma_free_coherent(q->dev, mem->size,
- mem->vaddr, mem->dma_handle);
- goto error;
+ if (!mem->cached)
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ pos = (unsigned long)mem->vaddr;
+
+ while (size > 0) {
+ page = virt_to_page((void *)pos);
+ if (NULL == page) {
+ dev_err(q->dev, "mmap: virt_to_page failed\n");
+ __videobuf_dc_free(q->dev, mem);
+ goto error;
+ }
+ retval = vm_insert_page(vma, start, page);
+ if (retval) {
+ dev_err(q->dev, "mmap: insert failed with error %d\n",
+ retval);
+ __videobuf_dc_free(q->dev, mem);
+ goto error;
+ }
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
}
- vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND;
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND;
vma->vm_private_data = map;
dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n",
map, q, vma->vm_start, vma->vm_end,
- (long int)buf->bsize,
- vma->vm_pgoff, buf->i);
+ (long int)buf->bsize, vma->vm_pgoff, buf->i);
videobuf_vm_open(vma);
@@ -328,12 +405,20 @@ error:
}
static struct videobuf_qtype_ops qops = {
- .magic = MAGIC_QTYPE_OPS,
+ .magic = MAGIC_QTYPE_OPS,
+ .alloc_vb = __videobuf_alloc_uncached,
+ .iolock = __videobuf_iolock,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .vaddr = __videobuf_to_vaddr,
+};
- .alloc_vb = __videobuf_alloc_vb,
- .iolock = __videobuf_iolock,
- .mmap_mapper = __videobuf_mmap_mapper,
- .vaddr = __videobuf_to_vaddr,
+static struct videobuf_qtype_ops qops_cached = {
+ .magic = MAGIC_QTYPE_OPS,
+ .alloc_vb = __videobuf_alloc_cached,
+ .iolock = __videobuf_iolock,
+ .sync = __videobuf_sync,
+ .mmap_mapper = __videobuf_mmap_mapper,
+ .vaddr = __videobuf_to_vaddr,
};
void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
@@ -351,6 +436,20 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q,
}
EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init);
+void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q,
+ const struct videobuf_queue_ops *ops,
+ struct device *dev,
+ spinlock_t *irqlock,
+ enum v4l2_buf_type type,
+ enum v4l2_field field,
+ unsigned int msize,
+ void *priv, struct mutex *ext_lock)
+{
+ videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize,
+ priv, &qops_cached, ext_lock);
+}
+EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached);
+
dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf)
{
struct videobuf_dma_contig_memory *mem = buf->priv;
@@ -389,7 +488,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q,
/* read() method */
if (mem->vaddr) {
- dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle);
+ __videobuf_dc_free(q->dev, mem);
mem->vaddr = NULL;
}
}
diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c
index 59cb54aa294..94d83a41381 100644
--- a/drivers/media/video/videobuf-dvb.c
+++ b/drivers/media/video/videobuf-dvb.c
@@ -45,7 +45,6 @@ static int videobuf_dvb_thread(void *data)
struct videobuf_dvb *dvb = data;
struct videobuf_buffer *buf;
unsigned long flags;
- int err;
void *outp;
dprintk("dvb thread started\n");
@@ -57,7 +56,7 @@ static int videobuf_dvb_thread(void *data)
buf = list_entry(dvb->dvbq.stream.next,
struct videobuf_buffer, stream);
list_del(&buf->stream);
- err = videobuf_waiton(&dvb->dvbq, buf, 0, 1);
+ videobuf_waiton(&dvb->dvbq, buf, 0, 1);
/* no more feeds left or stop_feed() asked us to quit */
if (0 == dvb->nfeeds)
diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 2e8f1df775b..9d4e9edbd2e 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -19,6 +19,9 @@
#include <linux/slab.h>
#include <linux/sched.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
#include <media/videobuf2-core.h>
static int debug;
@@ -1642,32 +1645,46 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q);
* For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor
* will be reported as available for writing.
*
+ * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any
+ * pending events.
+ *
* The return values from this function are intended to be directly returned
* from poll handler in driver.
*/
unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
{
- unsigned long flags;
- unsigned int ret;
+ struct video_device *vfd = video_devdata(file);
+ unsigned long req_events = poll_requested_events(wait);
struct vb2_buffer *vb = NULL;
+ unsigned int res = 0;
+ unsigned long flags;
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
+ struct v4l2_fh *fh = file->private_data;
+
+ if (v4l2_event_pending(fh))
+ res = POLLPRI;
+ else if (req_events & POLLPRI)
+ poll_wait(file, &fh->wait, wait);
+ }
/*
* Start file I/O emulator only if streaming API has not been used yet.
*/
if (q->num_buffers == 0 && q->fileio == NULL) {
- if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) {
- ret = __vb2_init_fileio(q, 1);
- if (ret)
- return POLLERR;
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) &&
+ (req_events & (POLLIN | POLLRDNORM))) {
+ if (__vb2_init_fileio(q, 1))
+ return res | POLLERR;
}
- if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) {
- ret = __vb2_init_fileio(q, 0);
- if (ret)
- return POLLERR;
+ if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) &&
+ (req_events & (POLLOUT | POLLWRNORM))) {
+ if (__vb2_init_fileio(q, 0))
+ return res | POLLERR;
/*
* Write to OUTPUT queue can be done immediately.
*/
- return POLLOUT | POLLWRNORM;
+ return res | POLLOUT | POLLWRNORM;
}
}
@@ -1675,7 +1692,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
* There is nothing to wait for if no buffers have already been queued.
*/
if (list_empty(&q->queued_list))
- return POLLERR;
+ return res | POLLERR;
poll_wait(file, &q->done_wq, wait);
@@ -1690,10 +1707,11 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
if (vb && (vb->state == VB2_BUF_STATE_DONE
|| vb->state == VB2_BUF_STATE_ERROR)) {
- return (V4L2_TYPE_IS_OUTPUT(q->type)) ? POLLOUT | POLLWRNORM :
- POLLIN | POLLRDNORM;
+ return (V4L2_TYPE_IS_OUTPUT(q->type)) ?
+ res | POLLOUT | POLLWRNORM :
+ res | POLLIN | POLLRDNORM;
}
- return 0;
+ return res;
}
EXPORT_SYMBOL_GPL(vb2_poll);
@@ -1839,7 +1857,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
* (multiplane buffers are not supported).
*/
if (q->bufs[0]->num_planes != 1) {
- fileio->req.count = 0;
ret = -EBUSY;
goto err_reqbufs;
}
@@ -1886,6 +1903,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read)
return ret;
err_reqbufs:
+ fileio->req.count = 0;
vb2_reqbufs(q, &fileio->req);
err_kfree:
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 5e8b0710105..0960d7f0d39 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -95,6 +95,16 @@ static struct vivi_fmt formats[] = {
.depth = 16,
},
{
+ .name = "4:2:2, packed, YVYU",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+ },
+ {
+ .name = "4:2:2, packed, VYUY",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+ },
+ {
.name = "RGB565 (LE)",
.fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
.depth = 16,
@@ -114,6 +124,26 @@ static struct vivi_fmt formats[] = {
.fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
.depth = 16,
},
+ {
+ .name = "RGB24 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .depth = 24,
+ },
+ {
+ .name = "RGB24 (BE)",
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .depth = 24,
+ },
+ {
+ .name = "RGB32 (LE)",
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .depth = 32,
+ },
+ {
+ .name = "RGB32 (BE)",
+ .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */
+ .depth = 32,
+ },
};
static struct vivi_fmt *get_format(struct v4l2_format *f)
@@ -170,6 +200,7 @@ struct vivi_dev {
struct v4l2_ctrl *gain;
};
struct v4l2_ctrl *volume;
+ struct v4l2_ctrl *alpha;
struct v4l2_ctrl *button;
struct v4l2_ctrl *boolean;
struct v4l2_ctrl *int32;
@@ -177,6 +208,7 @@ struct vivi_dev {
struct v4l2_ctrl *menu;
struct v4l2_ctrl *string;
struct v4l2_ctrl *bitmask;
+ struct v4l2_ctrl *int_menu;
spinlock_t slock;
struct mutex mutex;
@@ -203,8 +235,10 @@ struct vivi_dev {
enum v4l2_field field;
unsigned int field_count;
- u8 bars[9][3];
- u8 line[MAX_WIDTH * 4];
+ u8 bars[9][3];
+ u8 line[MAX_WIDTH * 8];
+ unsigned int pixelsize;
+ u8 alpha_component;
};
/* ------------------------------------------------------------------
@@ -283,6 +317,8 @@ static void precalculate_bars(struct vivi_dev *dev)
switch (dev->fmt->fourcc) {
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
is_yuv = 1;
break;
case V4L2_PIX_FMT_RGB565:
@@ -297,6 +333,11 @@ static void precalculate_bars(struct vivi_dev *dev)
g >>= 3;
b >>= 3;
break;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ break;
}
if (is_yuv) {
@@ -316,9 +357,11 @@ static void precalculate_bars(struct vivi_dev *dev)
#define TSTAMP_INPUT_X 10
#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X)
-static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos)
+/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */
+static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd)
{
u8 r_y, g_u, b_v;
+ u8 alpha = dev->alpha_component;
int color;
u8 *p;
@@ -326,46 +369,56 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos)
g_u = dev->bars[colorpos][1]; /* G or precalculated U */
b_v = dev->bars[colorpos][2]; /* B or precalculated V */
- for (color = 0; color < 4; color++) {
+ for (color = 0; color < dev->pixelsize; color++) {
p = buf + color;
switch (dev->fmt->fourcc) {
case V4L2_PIX_FMT_YUYV:
switch (color) {
case 0:
- case 2:
*p = r_y;
break;
case 1:
- *p = g_u;
- break;
- case 3:
- *p = b_v;
+ *p = odd ? b_v : g_u;
break;
}
break;
case V4L2_PIX_FMT_UYVY:
switch (color) {
+ case 0:
+ *p = odd ? b_v : g_u;
+ break;
case 1:
- case 3:
*p = r_y;
break;
+ }
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ switch (color) {
case 0:
- *p = g_u;
+ *p = r_y;
break;
- case 2:
- *p = b_v;
+ case 1:
+ *p = odd ? g_u : b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ switch (color) {
+ case 0:
+ *p = odd ? g_u : b_v;
+ break;
+ case 1:
+ *p = r_y;
break;
}
break;
case V4L2_PIX_FMT_RGB565:
switch (color) {
case 0:
- case 2:
*p = (g_u << 5) | b_v;
break;
case 1:
- case 3:
*p = (r_y << 3) | (g_u >> 3);
break;
}
@@ -373,11 +426,9 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos)
case V4L2_PIX_FMT_RGB565X:
switch (color) {
case 0:
- case 2:
*p = (r_y << 3) | (g_u >> 3);
break;
case 1:
- case 3:
*p = (g_u << 5) | b_v;
break;
}
@@ -385,24 +436,78 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos)
case V4L2_PIX_FMT_RGB555:
switch (color) {
case 0:
- case 2:
*p = (g_u << 5) | b_v;
break;
case 1:
- case 3:
- *p = (r_y << 2) | (g_u >> 3);
+ *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
break;
}
break;
case V4L2_PIX_FMT_RGB555X:
switch (color) {
case 0:
+ *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3);
+ break;
+ case 1:
+ *p = (g_u << 5) | b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ switch (color) {
+ case 0:
+ *p = r_y;
+ break;
+ case 1:
+ *p = g_u;
+ break;
case 2:
- *p = (r_y << 2) | (g_u >> 3);
+ *p = b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ switch (color) {
+ case 0:
+ *p = b_v;
break;
case 1:
+ *p = g_u;
+ break;
+ case 2:
+ *p = r_y;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ switch (color) {
+ case 0:
+ *p = alpha;
+ break;
+ case 1:
+ *p = r_y;
+ break;
+ case 2:
+ *p = g_u;
+ break;
case 3:
- *p = (g_u << 5) | b_v;
+ *p = b_v;
+ break;
+ }
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ switch (color) {
+ case 0:
+ *p = b_v;
+ break;
+ case 1:
+ *p = g_u;
+ break;
+ case 2:
+ *p = r_y;
+ break;
+ case 3:
+ *p = alpha;
break;
}
break;
@@ -414,10 +519,10 @@ static void precalculate_line(struct vivi_dev *dev)
{
int w;
- for (w = 0; w < dev->width * 2; w += 2) {
- int colorpos = (w / (dev->width / 8) % 8);
+ for (w = 0; w < dev->width * 2; w++) {
+ int colorpos = w / (dev->width / 8) % 8;
- gen_twopix(dev, dev->line + w * 2, colorpos);
+ gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1);
}
}
@@ -433,7 +538,7 @@ static void gen_text(struct vivi_dev *dev, char *basep,
/* Print stream time */
for (line = y; line < y + 16; line++) {
int j = 0;
- char *pos = basep + line * dev->width * 2 + x * 2;
+ char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize;
char *s;
for (s = text; *s; s++) {
@@ -443,9 +548,9 @@ static void gen_text(struct vivi_dev *dev, char *basep,
for (i = 0; i < 7; i++, j++) {
/* Draw white font on black background */
if (chr & (1 << (7 - i)))
- gen_twopix(dev, pos + j * 2, WHITE);
+ gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1);
else
- gen_twopix(dev, pos + j * 2, TEXT_BLACK);
+ gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1);
}
}
}
@@ -466,7 +571,9 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
return;
for (h = 0; h < hmax; h++)
- memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2);
+ memcpy(vbuf + h * wmax * dev->pixelsize,
+ dev->line + (dev->mv_count % wmax) * dev->pixelsize,
+ wmax * dev->pixelsize);
/* Updates stream time */
@@ -484,15 +591,16 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
gen_text(dev, vbuf, line++ * 16, 16, str);
gain = v4l2_ctrl_g_ctrl(dev->gain);
- mutex_lock(&dev->ctrl_handler.lock);
+ mutex_lock(dev->ctrl_handler.lock);
snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ",
dev->brightness->cur.val,
dev->contrast->cur.val,
dev->saturation->cur.val,
dev->hue->cur.val);
gen_text(dev, vbuf, line++ * 16, 16, str);
- snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d ",
- dev->autogain->cur.val, gain, dev->volume->cur.val);
+ snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ",
+ dev->autogain->cur.val, gain, dev->volume->cur.val,
+ dev->alpha->cur.val);
gen_text(dev, vbuf, line++ * 16, 16, str);
snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
dev->int32->cur.val,
@@ -503,8 +611,12 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
dev->boolean->cur.val,
dev->menu->qmenu[dev->menu->cur.val],
dev->string->cur.string);
- mutex_unlock(&dev->ctrl_handler.lock);
gen_text(dev, vbuf, line++ * 16, 16, str);
+ snprintf(str, sizeof(str), " integer_menu %lld, value %d ",
+ dev->int_menu->qmenu_int[dev->int_menu->cur.val],
+ dev->int_menu->cur.val);
+ gen_text(dev, vbuf, line++ * 16, 16, str);
+ mutex_unlock(dev->ctrl_handler.lock);
if (dev->button_pressed) {
dev->button_pressed--;
snprintf(str, sizeof(str), " button pressed!");
@@ -657,7 +769,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
struct vivi_dev *dev = vb2_get_drv_priv(vq);
unsigned long size;
- size = dev->width * dev->height * 2;
+ size = dev->width * dev->height * dev->pixelsize;
if (0 == *nbuffers)
*nbuffers = 32;
@@ -721,7 +833,7 @@ static int buffer_prepare(struct vb2_buffer *vb)
dev->height < 32 || dev->height > MAX_HEIGHT)
return -EINVAL;
- size = dev->width * dev->height * 2;
+ size = dev->width * dev->height * dev->pixelsize;
if (vb2_plane_size(vb, 0) < size) {
dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n",
__func__, vb2_plane_size(vb, 0), size);
@@ -915,6 +1027,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
}
dev->fmt = get_format(f);
+ dev->pixelsize = dev->fmt->depth / 8;
dev->width = f->fmt.pix.width;
dev->height = f->fmt.pix.height;
dev->field = f->fmt.pix.field;
@@ -1016,8 +1129,15 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler);
- if (ctrl == dev->button)
- dev->button_pressed = 30;
+ switch (ctrl->id) {
+ case V4L2_CID_ALPHA_COMPONENT:
+ dev->alpha_component = ctrl->val;
+ break;
+ default:
+ if (ctrl == dev->button)
+ dev->button_pressed = 30;
+ break;
+ }
return 0;
}
@@ -1039,17 +1159,10 @@ static unsigned int
vivi_poll(struct file *file, struct poll_table_struct *wait)
{
struct vivi_dev *dev = video_drvdata(file);
- struct v4l2_fh *fh = file->private_data;
struct vb2_queue *q = &dev->vb_vidq;
- unsigned int res;
dprintk(dev, 1, "%s\n", __func__);
- res = vb2_poll(q, file, wait);
- if (v4l2_event_pending(fh))
- res |= POLLPRI;
- else
- poll_wait(file, &fh->wait, wait);
- return res;
+ return vb2_poll(q, file, wait);
}
static int vivi_close(struct file *file)
@@ -1165,6 +1278,22 @@ static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
.step = 0,
};
+static const s64 vivi_ctrl_int_menu_values[] = {
+ 1, 1, 2, 3, 5, 8, 13, 21, 42,
+};
+
+static const struct v4l2_ctrl_config vivi_ctrl_int_menu = {
+ .ops = &vivi_ctrl_ops,
+ .id = VIVI_CID_CUSTOM_BASE + 7,
+ .name = "Integer menu",
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .min = 1,
+ .max = 8,
+ .def = 4,
+ .menu_skip_mask = 0x02,
+ .qmenu_int = vivi_ctrl_int_menu_values,
+};
+
static const struct v4l2_file_operations vivi_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
@@ -1252,6 +1381,7 @@ static int __init vivi_create_instance(int inst)
dev->fmt = &formats[0];
dev->width = 640;
dev->height = 480;
+ dev->pixelsize = dev->fmt->depth / 8;
hdl = &dev->ctrl_handler;
v4l2_ctrl_handler_init(hdl, 11);
dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
@@ -1268,6 +1398,8 @@ static int __init vivi_create_instance(int inst)
V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
V4L2_CID_GAIN, 0, 255, 1, 100);
+ dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops,
+ V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0);
dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL);
dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL);
dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL);
@@ -1275,6 +1407,7 @@ static int __init vivi_create_instance(int inst)
dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
+ dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL);
if (hdl->error) {
ret = hdl->error;
goto unreg_dev;
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
index 7fd7ac567e1..db2a6003a1c 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/video/w9966.c
@@ -62,6 +62,9 @@
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
#include <linux/parport.h>
/*#define DEBUG*/ /* Undef me for production */
@@ -104,6 +107,7 @@
struct w9966 {
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
unsigned char dev_state;
unsigned char i2c_state;
unsigned short ppmode;
@@ -567,7 +571,8 @@ static int cam_querycap(struct file *file, void *priv,
strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver));
strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card));
strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info));
- vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -595,67 +600,25 @@ static int cam_s_input(struct file *file, void *fh, unsigned int inp)
return (inp > 0) ? -EINVAL : 0;
}
-static int cam_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int cam_s_ctrl(struct v4l2_ctrl *ctrl)
{
- switch (qc->id) {
- case V4L2_CID_BRIGHTNESS:
- return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
- case V4L2_CID_CONTRAST:
- return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64);
- case V4L2_CID_SATURATION:
- return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64);
- case V4L2_CID_HUE:
- return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
- }
- return -EINVAL;
-}
-
-static int cam_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct w9966 *cam = video_drvdata(file);
- int ret = 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrl->value = cam->brightness;
- break;
- case V4L2_CID_CONTRAST:
- ctrl->value = cam->contrast;
- break;
- case V4L2_CID_SATURATION:
- ctrl->value = cam->color;
- break;
- case V4L2_CID_HUE:
- ctrl->value = cam->hue;
- break;
- default:
- ret = -EINVAL;
- break;
- }
- return ret;
-}
-
-static int cam_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct w9966 *cam = video_drvdata(file);
+ struct w9966 *cam =
+ container_of(ctrl->handler, struct w9966, hdl);
int ret = 0;
mutex_lock(&cam->lock);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
- cam->brightness = ctrl->value;
+ cam->brightness = ctrl->val;
break;
case V4L2_CID_CONTRAST:
- cam->contrast = ctrl->value;
+ cam->contrast = ctrl->val;
break;
case V4L2_CID_SATURATION:
- cam->color = ctrl->value;
+ cam->color = ctrl->val;
break;
case V4L2_CID_HUE:
- cam->hue = ctrl->value;
+ cam->hue = ctrl->val;
break;
default:
ret = -EINVAL;
@@ -813,6 +776,9 @@ out:
static const struct v4l2_file_operations w9966_fops = {
.owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
.unlocked_ioctl = video_ioctl2,
.read = w9966_v4l_read,
};
@@ -822,13 +788,17 @@ static const struct v4l2_ioctl_ops w9966_ioctl_ops = {
.vidioc_g_input = cam_g_input,
.vidioc_s_input = cam_s_input,
.vidioc_enum_input = cam_enum_input,
- .vidioc_queryctrl = cam_queryctrl,
- .vidioc_g_ctrl = cam_g_ctrl,
- .vidioc_s_ctrl = cam_s_ctrl,
.vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap,
.vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_ctrl_ops cam_ctrl_ops = {
+ .s_ctrl = cam_s_ctrl,
};
@@ -849,6 +819,20 @@ static int w9966_init(struct w9966 *cam, struct parport *port)
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
return -1;
}
+
+ v4l2_ctrl_handler_init(&cam->hdl, 4);
+ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
+ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+ V4L2_CID_CONTRAST, -64, 64, 1, 64);
+ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+ V4L2_CID_SATURATION, -64, 64, 1, 64);
+ v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops,
+ V4L2_CID_HUE, -128, 127, 1, 0);
+ if (cam->hdl.error) {
+ v4l2_err(v4l2_dev, "couldn't register controls\n");
+ return -1;
+ }
cam->pport = port;
cam->brightness = 128;
cam->contrast = 64;
@@ -898,6 +882,8 @@ static int w9966_init(struct w9966 *cam, struct parport *port)
cam->vdev.fops = &w9966_fops;
cam->vdev.ioctl_ops = &w9966_ioctl_ops;
cam->vdev.release = video_device_release_empty;
+ cam->vdev.ctrl_handler = &cam->hdl;
+ set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags);
video_set_drvdata(&cam->vdev, cam);
mutex_init(&cam->lock);
@@ -923,6 +909,8 @@ static void w9966_term(struct w9966 *cam)
w9966_set_state(cam, W9966_STATE_VDEV, 0);
}
+ v4l2_ctrl_handler_free(&cam->hdl);
+
/* Terminate from IEEE1284 mode and release pdev block */
if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) {
w9966_pdev_claim(cam);
diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c
index e86173bd132..a4cd504b8ee 100644
--- a/drivers/media/video/zoran/zoran_device.c
+++ b/drivers/media/video/zoran/zoran_device.c
@@ -542,11 +542,9 @@ void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count)
u32 *mask;
int x, y, width, height;
unsigned i, j, k;
- u32 reg;
/* fill mask with one bits */
memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
- reg = 0;
for (i = 0; i < count; ++i) {
/* pick up local copy of clip */
diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
index 4c09ab781ec..c5731093181 100644
--- a/drivers/media/video/zoran/zoran_driver.c
+++ b/drivers/media/video/zoran/zoran_driver.c
@@ -1131,8 +1131,14 @@ static int setup_fbuffer(struct zoran_fh *fh,
}
-static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height,
- struct v4l2_clip __user *clips, int clipcount, void __user *bitmap)
+static int setup_window(struct zoran_fh *fh,
+ int x,
+ int y,
+ int width,
+ int height,
+ struct v4l2_clip __user *clips,
+ unsigned int clipcount,
+ void __user *bitmap)
{
struct zoran *zr = fh->zr;
struct v4l2_clip *vcp = NULL;
@@ -1155,6 +1161,14 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height
return -EINVAL;
}
+ if (clipcount > 2048) {
+ dprintk(1,
+ KERN_ERR
+ "%s: %s - invalid clipcount\n",
+ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
/*
* The video front end needs 4-byte alinged line sizes, we correct that
* silently here if necessary
@@ -1218,7 +1232,7 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height
(width * height + 7) / 8)) {
return -EFAULT;
}
- } else if (clipcount > 0) {
+ } else if (clipcount) {
/* write our own bitmap from the clips */
vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4));
if (vcp == NULL) {
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index cd2e39fc4bf..e44cb330bbc 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -507,14 +507,12 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam,
const char *tmpbuf;
char *vbuf = videobuf_to_vmalloc(&buf->vb);
unsigned long last_frame;
- struct zr364xx_framei *frm;
if (!vbuf)
return;
last_frame = cam->last_frame;
if (last_frame != -1) {
- frm = &cam->buffer.frame[last_frame];
tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits;
switch (buf->fmt->fourcc) {
case V4L2_PIX_FMT_JPEG: