summaryrefslogtreecommitdiff
path: root/linux/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'linux/drivers/media')
-rw-r--r--linux/drivers/media/common/ir-keymaps.c103
-rw-r--r--linux/drivers/media/dvb/bt8xx/dst.c2
-rw-r--r--linux/drivers/media/dvb/dvb-usb/Kconfig6
-rw-r--r--linux/drivers/media/dvb/dvb-usb/af9015.c4
-rw-r--r--linux/drivers/media/dvb/dvb-usb/cxusb.c16
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dibusb-mc.c10
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c2
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h4
-rw-r--r--linux/drivers/media/dvb/dvb-usb/dw2102.c184
-rw-r--r--linux/drivers/media/dvb/firewire/firedtv-avc.c40
-rw-r--r--linux/drivers/media/dvb/frontends/Kconfig7
-rw-r--r--linux/drivers/media/dvb/frontends/Makefile1
-rw-r--r--linux/drivers/media/dvb/frontends/at76c651.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx22700.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx22702.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx24110.c2
-rw-r--r--linux/drivers/media/dvb/frontends/cx24113.c6
-rw-r--r--linux/drivers/media/dvb/frontends/dib7000p.c2
-rw-r--r--linux/drivers/media/dvb/frontends/dvb_dummy_fe.c6
-rw-r--r--linux/drivers/media/dvb/frontends/l64781.c2
-rw-r--r--linux/drivers/media/dvb/frontends/lgs8gl5.c2
-rw-r--r--linux/drivers/media/dvb/frontends/mt312.c2
-rw-r--r--linux/drivers/media/dvb/frontends/nxt6000.c2
-rw-r--r--linux/drivers/media/dvb/frontends/or51132.c2
-rw-r--r--linux/drivers/media/dvb/frontends/or51211.c2
-rw-r--r--linux/drivers/media/dvb/frontends/s5h1409.c2
-rw-r--r--linux/drivers/media/dvb/frontends/s5h1411.c2
-rw-r--r--linux/drivers/media/dvb/frontends/si21xx.c2
-rw-r--r--linux/drivers/media/dvb/frontends/sp8870.c2
-rw-r--r--linux/drivers/media/dvb/frontends/sp887x.c2
-rw-r--r--linux/drivers/media/dvb/frontends/stb6100.c5
-rw-r--r--linux/drivers/media/dvb/frontends/stv0288.c2
-rw-r--r--linux/drivers/media/dvb/frontends/stv0297.c2
-rw-r--r--linux/drivers/media/dvb/frontends/stv0299.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda10021.c5
-rw-r--r--linux/drivers/media/dvb/frontends/tda10048.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda1004x.c4
-rw-r--r--linux/drivers/media/dvb/frontends/tda10086.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda8083.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda80xx.c2
-rw-r--r--linux/drivers/media/dvb/frontends/tda8261.c4
-rw-r--r--linux/drivers/media/dvb/frontends/ves1820.c5
-rw-r--r--linux/drivers/media/dvb/frontends/ves1x93.c2
-rw-r--r--linux/drivers/media/dvb/frontends/zl10039.c308
-rw-r--r--linux/drivers/media/dvb/frontends/zl10039.h40
-rw-r--r--linux/drivers/media/dvb/frontends/zl10353.c14
-rw-r--r--linux/drivers/media/dvb/pluto2/pluto2.c2
-rw-r--r--linux/drivers/media/dvb/siano/Kconfig41
-rw-r--r--linux/drivers/media/dvb/siano/Makefile9
-rw-r--r--linux/drivers/media/dvb/siano/smscoreapi.c2
-rw-r--r--linux/drivers/media/dvb/siano/smsdvb.c44
-rw-r--r--linux/drivers/media/dvb/siano/smssdio.c54
-rw-r--r--linux/drivers/media/radio/Kconfig36
-rw-r--r--linux/drivers/media/radio/Makefile2
-rw-r--r--linux/drivers/media/radio/radio-si470x.c1870
-rw-r--r--linux/drivers/media/radio/si470x/Kconfig37
-rw-r--r--linux/drivers/media/radio/si470x/Makefile9
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x-common.c798
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x-i2c.c401
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x-usb.c988
-rw-r--r--linux/drivers/media/radio/si470x/radio-si470x.h226
-rw-r--r--linux/drivers/media/video/Kconfig8
-rw-r--r--linux/drivers/media/video/bw-qcam.c2
-rw-r--r--linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h8
-rw-r--r--linux/drivers/media/video/cx88/cx88-cards.c56
-rw-r--r--linux/drivers/media/video/cx88/cx88-dvb.c24
-rw-r--r--linux/drivers/media/video/cx88/cx88-input.c38
-rw-r--r--linux/drivers/media/video/cx88/cx88.h1
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-cards.c209
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-core.c3
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-dvb.c2
-rw-r--r--linux/drivers/media/video/em28xx/em28xx-video.c10
-rw-r--r--linux/drivers/media/video/em28xx/em28xx.h1
-rw-r--r--linux/drivers/media/video/gspca/Kconfig23
-rw-r--r--linux/drivers/media/video/gspca/Makefile2
-rw-r--r--linux/drivers/media/video/gspca/conex.c2
-rw-r--r--linux/drivers/media/video/gspca/etoms.c4
-rw-r--r--linux/drivers/media/video/gspca/jeilinj.c388
-rw-r--r--linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c4
-rw-r--r--linux/drivers/media/video/gspca/sonixj.c20
-rw-r--r--linux/drivers/media/video/gspca/spca501.c2
-rw-r--r--linux/drivers/media/video/gspca/spca506.c2
-rw-r--r--linux/drivers/media/video/gspca/stv06xx/stv06xx.c4
-rw-r--r--linux/drivers/media/video/gspca/sunplus.c4
-rw-r--r--linux/drivers/media/video/gspca/zc3xx.c2
-rw-r--r--linux/drivers/media/video/hdpvr/hdpvr-control.c24
-rw-r--r--linux/drivers/media/video/hdpvr/hdpvr-video.c2
-rw-r--r--linux/drivers/media/video/mt9m001.c435
-rw-r--r--linux/drivers/media/video/mt9m111.c524
-rw-r--r--linux/drivers/media/video/mt9t031.c491
-rw-r--r--linux/drivers/media/video/mt9v022.c434
-rw-r--r--linux/drivers/media/video/mx1_camera.c84
-rw-r--r--linux/drivers/media/video/mx3_camera.c226
-rw-r--r--linux/drivers/media/video/ov772x.c381
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-audio.c5
-rw-r--r--linux/drivers/media/video/pwc/pwc-if.c78
-rw-r--r--linux/drivers/media/video/pwc/pwc-v4l.c2
-rw-r--r--linux/drivers/media/video/pwc/pwc.h1
-rw-r--r--linux/drivers/media/video/pxa_camera.c364
-rw-r--r--linux/drivers/media/video/saa7134/Kconfig1
-rw-r--r--linux/drivers/media/video/saa7134/saa6752hs.c2
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-alsa.c14
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-cards.c49
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-dvb.c17
-rw-r--r--linux/drivers/media/video/saa7134/saa7134-input.c9
-rw-r--r--linux/drivers/media/video/saa7134/saa7134.h1
-rw-r--r--linux/drivers/media/video/sh_mobile_ceu_camera.c1063
-rw-r--r--linux/drivers/media/video/soc_camera.c726
-rw-r--r--linux/drivers/media/video/soc_camera_platform.c164
-rw-r--r--linux/drivers/media/video/stk-webcam.c5
-rw-r--r--linux/drivers/media/video/tuner-core.c4
-rw-r--r--linux/drivers/media/video/tveeprom.c4
-rw-r--r--linux/drivers/media/video/tw9910.c361
-rw-r--r--linux/drivers/media/video/uvc/uvc_driver.c6
-rw-r--r--linux/drivers/media/video/v4l1-compat.c5
-rw-r--r--linux/drivers/media/video/v4l2-ioctl.c15
-rw-r--r--linux/drivers/media/video/zoran/zoran_card.c2
-rw-r--r--linux/drivers/media/video/zr364xx.c2
118 files changed, 7502 insertions, 4161 deletions
diff --git a/linux/drivers/media/common/ir-keymaps.c b/linux/drivers/media/common/ir-keymaps.c
index 56fa64b4c..0b1566d34 100644
--- a/linux/drivers/media/common/ir-keymaps.c
+++ b/linux/drivers/media/common/ir-keymaps.c
@@ -2801,6 +2801,61 @@ IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec);
+
+/* Terratec Cinergy Hybrid T USB XS
+ Devin Heitmueller <dheitmueller@linuxtv.org>
+ */
+IR_KEYTAB_TYPE ir_codes_terratec_cinergy_xs[IR_KEYTAB_SIZE] = {
+ [0x41] = KEY_HOME,
+ [0x01] = KEY_POWER,
+ [0x42] = KEY_MENU,
+ [0x02] = KEY_1,
+ [0x03] = KEY_2,
+ [0x04] = KEY_3,
+ [0x43] = KEY_SUBTITLE,
+ [0x05] = KEY_4,
+ [0x06] = KEY_5,
+ [0x07] = KEY_6,
+ [0x44] = KEY_TEXT,
+ [0x08] = KEY_7,
+ [0x09] = KEY_8,
+ [0x0a] = KEY_9,
+ [0x45] = KEY_DELETE,
+ [0x0b] = KEY_TUNER,
+ [0x0c] = KEY_0,
+ [0x0d] = KEY_MODE,
+ [0x46] = KEY_TV,
+ [0x47] = KEY_DVD,
+ [0x49] = KEY_VIDEO,
+ [0x4b] = KEY_AUX,
+ [0x10] = KEY_UP,
+ [0x11] = KEY_LEFT,
+ [0x12] = KEY_OK,
+ [0x13] = KEY_RIGHT,
+ [0x14] = KEY_DOWN,
+ [0x0f] = KEY_EPG,
+ [0x16] = KEY_INFO,
+ [0x4d] = KEY_BACKSPACE,
+ [0x1c] = KEY_VOLUMEUP,
+ [0x4c] = KEY_PLAY,
+ [0x1b] = KEY_CHANNELUP,
+ [0x1e] = KEY_VOLUMEDOWN,
+ [0x1d] = KEY_MUTE,
+ [0x1f] = KEY_CHANNELDOWN,
+ [0x17] = KEY_RED,
+ [0x18] = KEY_GREEN,
+ [0x19] = KEY_YELLOW,
+ [0x1a] = KEY_BLUE,
+ [0x58] = KEY_RECORD,
+ [0x48] = KEY_STOP,
+ [0x40] = KEY_PAUSE,
+ [0x54] = KEY_LAST,
+ [0x4e] = KEY_REWIND,
+ [0x4f] = KEY_FASTFORWARD,
+ [0x5c] = KEY_NEXT,
+};
+EXPORT_SYMBOL_GPL(ir_codes_terratec_cinergy_xs);
+
/* EVGA inDtube
Devin Heitmueller <devin.heitmueller@gmail.com>
*/
@@ -2823,3 +2878,51 @@ IR_KEYTAB_TYPE ir_codes_evga_indtube[IR_KEYTAB_SIZE] = {
[0x13] = KEY_CAMERA,
};
EXPORT_SYMBOL_GPL(ir_codes_evga_indtube);
+
+IR_KEYTAB_TYPE ir_codes_videomate_s350[IR_KEYTAB_SIZE] = {
+ [0x00] = KEY_TV,
+ [0x01] = KEY_DVD,
+ [0x04] = KEY_RECORD,
+ [0x05] = KEY_VIDEO, /* TV/Video */
+ [0x07] = KEY_STOP,
+ [0x08] = KEY_PLAYPAUSE,
+ [0x0a] = KEY_REWIND,
+ [0x0f] = KEY_FASTFORWARD,
+ [0x10] = KEY_CHANNELUP,
+ [0x12] = KEY_VOLUMEUP,
+ [0x13] = KEY_CHANNELDOWN,
+ [0x14] = KEY_MUTE,
+ [0x15] = KEY_VOLUMEDOWN,
+ [0x16] = KEY_1,
+ [0x17] = KEY_2,
+ [0x18] = KEY_3,
+ [0x19] = KEY_4,
+ [0x1a] = KEY_5,
+ [0x1b] = KEY_6,
+ [0x1c] = KEY_7,
+ [0x1d] = KEY_8,
+ [0x1e] = KEY_9,
+ [0x1f] = KEY_0,
+ [0x21] = KEY_SLEEP,
+ [0x24] = KEY_ZOOM,
+ [0x25] = KEY_LAST, /* Recall */
+ [0x26] = KEY_SUBTITLE, /* CC */
+ [0x27] = KEY_LANGUAGE, /* MTS */
+ [0x29] = KEY_CHANNEL, /* SURF */
+ [0x2b] = KEY_A,
+ [0x2c] = KEY_B,
+ [0x2f] = KEY_SHUFFLE, /* Snapshot */
+ [0x23] = KEY_RADIO,
+ [0x02] = KEY_PREVIOUSSONG,
+ [0x06] = KEY_NEXTSONG,
+ [0x03] = KEY_EPG,
+ [0x09] = KEY_SETUP,
+ [0x22] = KEY_BACKSPACE,
+ [0x0c] = KEY_UP,
+ [0x0e] = KEY_DOWN,
+ [0x0b] = KEY_LEFT,
+ [0x0d] = KEY_RIGHT,
+ [0x11] = KEY_ENTER,
+ [0x20] = KEY_TEXT,
+};
+EXPORT_SYMBOL_GPL(ir_codes_videomate_s350);
diff --git a/linux/drivers/media/dvb/bt8xx/dst.c b/linux/drivers/media/dvb/bt8xx/dst.c
index fec1d77fa..91353a6fa 100644
--- a/linux/drivers/media/dvb/bt8xx/dst.c
+++ b/linux/drivers/media/dvb/bt8xx/dst.c
@@ -1059,7 +1059,7 @@ static int dst_get_tuner_info(struct dst_state *state)
dprintk(verbose, DST_ERROR, 1, "DST type has TS=188");
}
if (state->board_info[0] == 0xbc) {
- if (state->type_flags != DST_TYPE_IS_ATSC)
+ if (state->dst_type != DST_TYPE_IS_ATSC)
state->type_flags |= DST_TYPE_HAS_TS188;
else
state->type_flags |= DST_TYPE_HAS_NEWTUNE_2;
diff --git a/linux/drivers/media/dvb/dvb-usb/Kconfig b/linux/drivers/media/dvb/dvb-usb/Kconfig
index 496c1a370..8b8bc04ee 100644
--- a/linux/drivers/media/dvb/dvb-usb/Kconfig
+++ b/linux/drivers/media/dvb/dvb-usb/Kconfig
@@ -253,7 +253,7 @@ config DVB_USB_AF9005_REMOTE
Afatech AF9005 based receiver.
config DVB_USB_DW2102
- tristate "DvbWorld DVB-S/S2 USB2.0 support"
+ tristate "DvbWorld & TeVii DVB-S/S2 USB2.0 support"
depends on DVB_USB
select DVB_PLL if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
@@ -262,9 +262,11 @@ config DVB_USB_DW2102
select DVB_CX24116 if !DVB_FE_CUSTOMISE
select DVB_SI21XX if !DVB_FE_CUSTOMISE
select DVB_TDA10021 if !DVB_FE_CUSTOMISE
+ select DVB_MT312 if !DVB_FE_CUSTOMISE
+ select DVB_ZL10039 if !DVB_FE_CUSTOMISE
help
Say Y here to support the DvbWorld DVB-S/S2 USB2.0 receivers
- and the TeVii S650.
+ and the TeVii S650, S630.
config DVB_USB_CINERGY_T2
tristate "Terratec CinergyT2/qanu USB 2.0 DVB-T receiver"
diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c
index 7798aabaa..21815367f 100644
--- a/linux/drivers/media/dvb/dvb-usb/af9015.c
+++ b/linux/drivers/media/dvb/dvb-usb/af9015.c
@@ -1267,6 +1267,7 @@ static struct usb_device_id af9015_usb_table[] = {
{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)},
{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)},
{USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)},
+/* 25 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)},
{0},
};
MODULE_DEVICE_TABLE(usb, af9015_usb_table);
@@ -1347,7 +1348,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
{
.name = "KWorld PlusTV Dual DVB-T Stick " \
"(DVB-T 399U)",
- .cold_ids = {&af9015_usb_table[4], NULL},
+ .cold_ids = {&af9015_usb_table[4],
+ &af9015_usb_table[25], NULL},
.warm_ids = {NULL},
},
{
diff --git a/linux/drivers/media/dvb/dvb-usb/cxusb.c b/linux/drivers/media/dvb/dvb-usb/cxusb.c
index f467cad8a..05da84c90 100644
--- a/linux/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/linux/drivers/media/dvb/dvb-usb/cxusb.c
@@ -38,7 +38,7 @@
#include "mxl5005s.h"
#include "dib7000p.h"
#include "dib0070.h"
-#include "lgs8gl5.h"
+#include "lgs8gxx.h"
/* debug */
static int dvb_usb_cxusb_debug;
@@ -1097,8 +1097,18 @@ static int cxusb_nano2_frontend_attach(struct dvb_usb_adapter *adap)
return -EIO;
}
-static struct lgs8gl5_config lgs8gl5_cfg = {
+static struct lgs8gxx_config d680_lgs8gl5_cfg = {
+ .prod = LGS8GXX_PROD_LGS8GL5,
.demod_address = 0x19,
+ .serial_ts = 0,
+ .ts_clk_pol = 0,
+ .ts_clk_gated = 1,
+ .if_clk_freq = 30400, /* 30.4 MHz */
+ .if_freq = 5725, /* 5.725 MHz */
+ .if_neg_center = 0,
+ .ext_adc = 0,
+ .adc_signed = 0,
+ .if_neg_edge = 0,
};
static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
@@ -1138,7 +1148,7 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap)
msleep(100);
/* Attach frontend */
- adap->fe = dvb_attach(lgs8gl5_attach, &lgs8gl5_cfg, &d->i2c_adap);
+ adap->fe = dvb_attach(lgs8gxx_attach, &d680_lgs8gl5_cfg, &d->i2c_adap);
if (adap->fe == NULL)
return -EIO;
diff --git a/linux/drivers/media/dvb/dvb-usb/dibusb-mc.c b/linux/drivers/media/dvb/dvb-usb/dibusb-mc.c
index 059cec955..a05b9f875 100644
--- a/linux/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/linux/drivers/media/dvb/dvb-usb/dibusb-mc.c
@@ -42,6 +42,8 @@ static struct usb_device_id dibusb_dib3000mc_table [] = {
/* 11 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_WARM) },
/* 12 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_COLD) },
/* 13 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_WARM) },
+/* 14 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD) },
+/* 15 */ { USB_DEVICE(USB_VID_HUMAX_COEX, USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table);
@@ -66,7 +68,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
/* parameter for the MPEG2-data transfer */
.stream = {
.type = USB_BULK,
- .count = 7,
+ .count = 8,
.endpoint = 0x06,
.u = {
.bulk = {
@@ -88,7 +90,7 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
.generic_bulk_ctrl_endpoint = 0x01,
- .num_device_descs = 7,
+ .num_device_descs = 8,
.devices = {
{ "DiBcom USB2.0 DVB-T reference design (MOD3000P)",
{ &dibusb_dib3000mc_table[0], NULL },
@@ -119,6 +121,10 @@ static struct dvb_usb_device_properties dibusb_mc_properties = {
{ &dibusb_dib3000mc_table[12], NULL },
{ &dibusb_dib3000mc_table[13], NULL },
},
+ { "Humax/Coex DVB-T USB Stick 2.0 High Speed",
+ { &dibusb_dib3000mc_table[14], NULL },
+ { &dibusb_dib3000mc_table[15], NULL },
+ },
{ NULL },
}
};
diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
index 326f76089..cead089bb 100644
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c
@@ -19,7 +19,7 @@ int dvb_usb_i2c_init(struct dvb_usb_device *d)
return -EINVAL;
}
- strncpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
+ strlcpy(d->i2c_adap.name, d->desc->name, sizeof(d->i2c_adap.name));
d->i2c_adap.class = I2C_CLASS_TV_DIGITAL,
d->i2c_adap.algo = d->props.i2c_algo;
d->i2c_adap.algo_data = NULL;
diff --git a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 6eccce1a7..3324a73d5 100644
--- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -58,6 +58,7 @@
#define USB_VID_GIGABYTE 0x1044
#define USB_VID_YUAN 0x1164
#define USB_VID_XTENSIONS 0x1ae7
+#define USB_VID_HUMAX_COEX 0x10b9
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
@@ -103,6 +104,7 @@
#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_INTEL_CE9500 0x9500
#define USB_PID_KWORLD_399U 0xe399
+#define USB_PID_KWORLD_399U_2 0xe400
#define USB_PID_KWORLD_395U 0xe396
#define USB_PID_KWORLD_395U_2 0xe39b
#define USB_PID_KWORLD_395U_3 0xe395
@@ -260,5 +262,7 @@
#define USB_PID_SONY_PLAYTV 0x0003
#define USB_PID_ELGATO_EYETV_DTT 0x0021
#define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000
+#define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001
#endif
diff --git a/linux/drivers/media/dvb/dvb-usb/dw2102.c b/linux/drivers/media/dvb/dvb-usb/dw2102.c
index 0fc9be43a..c8bdb2e78 100644
--- a/linux/drivers/media/dvb/dvb-usb/dw2102.c
+++ b/linux/drivers/media/dvb/dvb-usb/dw2102.c
@@ -1,6 +1,6 @@
/* DVB USB framework compliant Linux driver for the
* DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101,
-* TeVii S600, S650 Cards
+* TeVii S600, S630, S650 Cards
* Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by)
*
* This program is free software; you can redistribute it and/or modify it
@@ -18,6 +18,8 @@
#include "eds1547.h"
#include "cx24116.h"
#include "tda1002x.h"
+#include "mt312.h"
+#include "zl10039.h"
#ifndef USB_PID_DW2102
#define USB_PID_DW2102 0x2102
@@ -39,6 +41,10 @@
#define USB_PID_TEVII_S650 0xd650
#endif
+#ifndef USB_PID_TEVII_S630
+#define USB_PID_TEVII_S630 0xd630
+#endif
+
#define DW210X_READ_MSG 0
#define DW210X_WRITE_MSG 1
@@ -436,6 +442,69 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],
return num;
}
+static int s630_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;
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ switch (num) {
+ case 2: { /* read */
+ u8 ibuf[msg[1].len], obuf[3];
+ obuf[0] = msg[1].len;
+ obuf[1] = (msg[0].addr << 1);
+ obuf[2] = msg[0].buf[0];
+
+ ret = dw210x_op_rw(d->udev, 0x90, 0, 0,
+ obuf, 3, DW210X_WRITE_MSG);
+ msleep(5);
+ ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
+ ibuf, msg[1].len, DW210X_READ_MSG);
+ memcpy(msg[1].buf, ibuf, msg[1].len);
+ break;
+ }
+ case 1:
+ switch (msg[0].addr) {
+ case 0x60:
+ case 0x0e: {
+ /* write to zl10313, zl10039 register, */
+ u8 obuf[msg[0].len + 2];
+ obuf[0] = msg[0].len + 1;
+ obuf[1] = (msg[0].addr << 1);
+ memcpy(obuf + 2, msg[0].buf, msg[0].len);
+ ret = dw210x_op_rw(d->udev, 0x80, 0, 0,
+ obuf, msg[0].len + 2, DW210X_WRITE_MSG);
+ break;
+ }
+ case (DW2102_RC_QUERY): {
+ u8 ibuf[4];
+ ret = dw210x_op_rw(d->udev, 0xb8, 0, 0,
+ ibuf, 4, DW210X_READ_MSG);
+ msg[0].buf[0] = ibuf[3];
+ break;
+ }
+ case (DW2102_VOLTAGE_CTRL): {
+ u8 obuf[2];
+ obuf[0] = 0x03;
+ obuf[1] = msg[0].buf[0];
+ ret = dw210x_op_rw(d->udev, 0x8a, 0, 0,
+ obuf, 2, DW210X_WRITE_MSG);
+ break;
+ }
+ }
+
+ break;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+ return num;
+}
+
static u32 dw210x_i2c_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C;
@@ -481,6 +550,14 @@ static struct i2c_algorithm dw3101_i2c_algo = {
#endif
};
+static struct i2c_algorithm s630_i2c_algo = {
+ .master_xfer = s630_i2c_transfer,
+ .functionality = dw210x_i2c_func,
+#ifdef NEED_ALGO_CONTROL
+ .algo_control = dummy_algo_control,
+#endif
+};
+
static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
{
int i;
@@ -505,6 +582,37 @@ static int dw210x_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
return 0;
};
+static int s630_read_mac_address(struct dvb_usb_device *d, u8 mac[6])
+{
+ int i, ret;
+ u8 buf[3], eeprom[256], eepromline[16];
+
+ for (i = 0; i < 256; i++) {
+ buf[0] = 1;
+ buf[1] = 0xa0;
+ buf[2] = i;
+ ret = dw210x_op_rw(d->udev, 0x90, 0, 0,
+ buf, 3, DW210X_WRITE_MSG);
+ ret = dw210x_op_rw(d->udev, 0x91, 0, 0,
+ buf, 1, DW210X_READ_MSG);
+ if (ret < 0) {
+ err("read eeprom failed.");
+ return -1;
+ } else {
+ eepromline[i % 16] = buf[0];
+ eeprom[i] = buf[0];
+ }
+
+ if ((i % 16) == 15) {
+ deb_xfer("%02x: ", i - 15);
+ debug_dump(eepromline, 16, deb_xfer);
+ }
+ }
+
+ memcpy(mac, eeprom + 16, 6);
+ return 0;
+};
+
static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
static u8 command_13v[1] = {0x00};
@@ -550,6 +658,10 @@ static struct tda10023_config dw3101_tda10023_config = {
.invert = 1,
};
+static struct mt312_config zl313_config = {
+ .demod_address = 0x0e,
+};
+
static int dw2104_frontend_attach(struct dvb_usb_adapter *d)
{
if ((d->fe = dvb_attach(cx24116_attach, &dw2104_config,
@@ -611,6 +723,18 @@ static int dw3101_frontend_attach(struct dvb_usb_adapter *d)
return -EIO;
}
+static int s630_frontend_attach(struct dvb_usb_adapter *d)
+{
+ d->fe = dvb_attach(mt312_attach, &zl313_config,
+ &d->dev->i2c_adap);
+ if (d->fe != NULL) {
+ d->fe->ops.set_voltage = dw210x_set_voltage;
+ info("Attached zl10313!\n");
+ return 0;
+ }
+ return -EIO;
+}
+
static int dw2102_tuner_attach(struct dvb_usb_adapter *adap)
{
dvb_attach(dvb_pll_attach, adap->fe, 0x60,
@@ -634,6 +758,14 @@ static int dw3101_tuner_attach(struct dvb_usb_adapter *adap)
return 0;
}
+static int s630_zl10039_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ dvb_attach(zl10039_attach, adap->fe, 0x60,
+ &adap->dev->i2c_adap);
+
+ return 0;
+}
+
static struct dvb_usb_rc_key dw210x_rc_keys[] = {
{ 0xf8, 0x0a, KEY_Q }, /*power*/
{ 0xf8, 0x0c, KEY_M }, /*mute*/
@@ -778,7 +910,7 @@ static int dw2102_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
}
*state = REMOTE_NO_KEY_PRESSED;
- if (dw2102_i2c_transfer(&d->i2c_adap, &msg, 1) == 1) {
+ if (d->props.i2c_algo->master_xfer(&d->i2c_adap, &msg, 1) == 1) {
for (i = 0; i < keymap_size ; i++) {
if (keymap[i].data == msg.buf[0]) {
*state = REMOTE_KEY_PRESSED;
@@ -807,6 +939,7 @@ static struct usb_device_id dw2102_table[] = {
{USB_DEVICE(0x9022, USB_PID_TEVII_S650)},
{USB_DEVICE(USB_VID_TERRATEC, USB_PID_CINERGY_S)},
{USB_DEVICE(USB_VID_CYPRESS, USB_PID_DW3101)},
+ {USB_DEVICE(0x9022, USB_PID_TEVII_S630)},
{ }
};
@@ -821,6 +954,7 @@ static int dw2102_load_firmware(struct usb_device *dev,
u8 reset16[] = {0, 0, 0, 0, 0, 0, 0};
const struct firmware *fw;
const char *filename = "dvb-usb-dw2101.fw";
+
switch (dev->descriptor.idProduct) {
case 0x2101:
ret = request_firmware(&fw, filename, &dev->dev);
@@ -1068,6 +1202,48 @@ static struct dvb_usb_device_properties dw3101_properties = {
}
};
+static struct dvb_usb_device_properties s630_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .firmware = "dvb-usb-s630.fw",
+ .no_reconnect = 1,
+
+ .i2c_algo = &s630_i2c_algo,
+ .rc_key_map = tevii_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(tevii_rc_keys),
+ .rc_interval = 150,
+ .rc_query = dw2102_rc_query,
+
+ .generic_bulk_ctrl_endpoint = 0x81,
+ .num_adapters = 1,
+ .download_firmware = dw2102_load_firmware,
+ .read_mac_address = s630_read_mac_address,
+ .adapter = {
+ {
+ .frontend_attach = s630_frontend_attach,
+ .streaming_ctrl = NULL,
+ .tuner_attach = s630_zl10039_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ }
+ },
+ .num_device_descs = 1,
+ .devices = {
+ {"TeVii S630 USB",
+ {&dw2102_table[6], NULL},
+ {NULL},
+ },
+ }
+};
+
static int dw2102_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -1076,6 +1252,8 @@ static int dw2102_probe(struct usb_interface *intf,
0 == dvb_usb_device_init(intf, &dw2104_properties,
THIS_MODULE, NULL, adapter_nr) ||
0 == dvb_usb_device_init(intf, &dw3101_properties,
+ THIS_MODULE, NULL, adapter_nr) ||
+ 0 == dvb_usb_device_init(intf, &s630_properties,
THIS_MODULE, NULL, adapter_nr)) {
return 0;
}
@@ -1109,6 +1287,6 @@ module_exit(dw2102_module_exit);
MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by");
MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104,"
" DVB-C 3101 USB2.0,"
- " TeVii S600, S650 USB2.0 devices");
+ " TeVii S600, S630, S650 USB2.0 devices");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/firewire/firedtv-avc.c b/linux/drivers/media/dvb/firewire/firedtv-avc.c
index 620fa2c65..a2041df5f 100644
--- a/linux/drivers/media/dvb/firewire/firedtv-avc.c
+++ b/linux/drivers/media/dvb/firewire/firedtv-avc.c
@@ -259,6 +259,26 @@ int avc_recv(struct firedtv *fdtv, void *data, size_t length)
return 0;
}
+static int add_pid_filter(struct firedtv *fdtv, u8 *operand)
+{
+ int i, n, pos = 1;
+
+ for (i = 0, n = 0; i < 16; i++) {
+ if (test_bit(i, &fdtv->channel_active)) {
+ operand[pos++] = 0x13; /* flowfunction relay */
+ operand[pos++] = 0x80; /* dsd_sel_spec_valid_flags -> PID */
+ operand[pos++] = (fdtv->channel_pid[i] >> 8) & 0x1f;
+ operand[pos++] = fdtv->channel_pid[i] & 0xff;
+ operand[pos++] = 0x00; /* tableID */
+ operand[pos++] = 0x00; /* filter_length */
+ n++;
+ }
+ }
+ operand[0] = n;
+
+ return pos;
+}
+
/*
* tuning command for setting the relative LNB frequency
* (not supported by the AVC standard)
@@ -321,7 +341,8 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv,
}
}
-static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params,
+static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv,
+ struct dvb_frontend_parameters *params,
struct avc_command_frame *c)
{
c->opcode = AVC_OPCODE_DSD;
@@ -383,13 +404,13 @@ static void avc_tuner_dsd_dvb_c(struct dvb_frontend_parameters *params,
c->operand[20] = 0x00;
c->operand[21] = 0x00;
- /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */
- c->operand[22] = 0x00;
- c->length = 28;
+ /* Add PIDs to filter */
+ c->length = ALIGN(22 + add_pid_filter(fdtv, &c->operand[22]) + 3, 4);
}
-static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params,
+static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv,
+ struct dvb_frontend_parameters *params,
struct avc_command_frame *c)
{
struct dvb_ofdm_parameters *ofdm = &params->u.ofdm;
@@ -486,10 +507,9 @@ static void avc_tuner_dsd_dvb_t(struct dvb_frontend_parameters *params,
c->operand[15] = 0x00; /* network_ID[0] */
c->operand[16] = 0x00; /* network_ID[1] */
- /* Nr_of_dsd_sel_specs = 0 -> no PIDs are transmitted */
- c->operand[17] = 0x00;
- c->length = 24;
+ /* Add PIDs to filter */
+ c->length = ALIGN(17 + add_pid_filter(fdtv, &c->operand[17]) + 3, 4);
}
int avc_tuner_dsd(struct firedtv *fdtv,
@@ -507,8 +527,8 @@ int avc_tuner_dsd(struct firedtv *fdtv,
switch (fdtv->type) {
case FIREDTV_DVB_S:
case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break;
- case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(params, c); break;
- case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(params, c); break;
+ case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(fdtv, params, c); break;
+ case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(fdtv, params, c); break;
default:
BUG();
}
diff --git a/linux/drivers/media/dvb/frontends/Kconfig b/linux/drivers/media/dvb/frontends/Kconfig
index be967ac09..b794e860b 100644
--- a/linux/drivers/media/dvb/frontends/Kconfig
+++ b/linux/drivers/media/dvb/frontends/Kconfig
@@ -81,6 +81,13 @@ config DVB_ZL10036
help
A DVB-S tuner module. Say Y when you want to support this frontend.
+config DVB_ZL10039
+ tristate "Zarlink ZL10039 silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
config DVB_S5H1420
tristate "Samsung S5H1420 based"
depends on DVB_CORE && I2C
diff --git a/linux/drivers/media/dvb/frontends/Makefile b/linux/drivers/media/dvb/frontends/Makefile
index 832473c1e..3b49d37ab 100644
--- a/linux/drivers/media/dvb/frontends/Makefile
+++ b/linux/drivers/media/dvb/frontends/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_DVB_SP887X) += sp887x.o
obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
obj-$(CONFIG_DVB_MT352) += mt352.o
obj-$(CONFIG_DVB_ZL10036) += zl10036.o
+obj-$(CONFIG_DVB_ZL10039) += zl10039.o
obj-$(CONFIG_DVB_ZL10353) += zl10353.o
obj-$(CONFIG_DVB_CX22702) += cx22702.o
obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o
diff --git a/linux/drivers/media/dvb/frontends/at76c651.c b/linux/drivers/media/dvb/frontends/at76c651.c
index 33486d54c..510e08f5a 100644
--- a/linux/drivers/media/dvb/frontends/at76c651.c
+++ b/linux/drivers/media/dvb/frontends/at76c651.c
@@ -369,7 +369,7 @@ struct dvb_frontend* at76c651_attach(const struct at76c651_config* config,
struct at76c651_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct at76c651_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct at76c651_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/cx22700.c b/linux/drivers/media/dvb/frontends/cx22700.c
index ace5cb171..fbd838eca 100644
--- a/linux/drivers/media/dvb/frontends/cx22700.c
+++ b/linux/drivers/media/dvb/frontends/cx22700.c
@@ -380,7 +380,7 @@ struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
struct cx22700_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx22700_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/cx22702.c b/linux/drivers/media/dvb/frontends/cx22702.c
index 5d1abe34b..00b5c7e91 100644
--- a/linux/drivers/media/dvb/frontends/cx22702.c
+++ b/linux/drivers/media/dvb/frontends/cx22702.c
@@ -580,7 +580,7 @@ struct dvb_frontend *cx22702_attach(const struct cx22702_config *config,
struct cx22702_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/cx24110.c b/linux/drivers/media/dvb/frontends/cx24110.c
index 87ae29db0..ffbcfabd8 100644
--- a/linux/drivers/media/dvb/frontends/cx24110.c
+++ b/linux/drivers/media/dvb/frontends/cx24110.c
@@ -598,7 +598,7 @@ struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
int ret;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/cx24113.c b/linux/drivers/media/dvb/frontends/cx24113.c
index e4fd533a4..075b2b57c 100644
--- a/linux/drivers/media/dvb/frontends/cx24113.c
+++ b/linux/drivers/media/dvb/frontends/cx24113.c
@@ -303,6 +303,7 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
{
s32 N;
s64 F;
+ u64 dividend;
u8 R, r;
u8 vcodiv;
u8 factor;
@@ -346,7 +347,10 @@ static void cx24113_calc_pll_nf(struct cx24113_state *state, u16 *n, s32 *f)
F = freq_hz;
F *= (u64) (R * vcodiv * 262144);
dprintk("1 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
- do_div(F, state->config->xtal_khz*1000 * factor * 2);
+ /* do_div needs an u64 as first argument */
+ dividend = F;
+ do_div(dividend, state->config->xtal_khz * 1000 * factor * 2);
+ F = dividend;
dprintk("2 N: %d, F: %lld, R: %d\n", N, (long long)F, R);
F -= (N + 32) * 262144;
diff --git a/linux/drivers/media/dvb/frontends/dib7000p.c b/linux/drivers/media/dvb/frontends/dib7000p.c
index ebb7594e3..d56de75db 100644
--- a/linux/drivers/media/dvb/frontends/dib7000p.c
+++ b/linux/drivers/media/dvb/frontends/dib7000p.c
@@ -909,7 +909,7 @@ static void dib7000p_spur_protect(struct dib7000p_state *state, u32 rf_khz, u32
255, 255, 255, 255, 255, 255};
u32 xtal = state->cfg.bw->xtal_hz / 1000;
- int f_rel = ( (rf_khz + xtal/2) / xtal) * xtal - rf_khz;
+ int f_rel = DIV_ROUND_CLOSEST(rf_khz, xtal) * xtal - rf_khz;
int k;
int coef_re[8],coef_im[8];
int bw_khz = bw;
diff --git a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c
index db8a937cc..a7fc7e53a 100644
--- a/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c
+++ b/linux/drivers/media/dvb/frontends/dvb_dummy_fe.c
@@ -117,7 +117,7 @@ struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
@@ -137,7 +137,7 @@ struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
@@ -157,7 +157,7 @@ struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
struct dvb_dummy_fe_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL);
if (state == NULL) goto error;
/* create dvb_frontend */
diff --git a/linux/drivers/media/dvb/frontends/l64781.c b/linux/drivers/media/dvb/frontends/l64781.c
index e1e70e9e0..3051b64aa 100644
--- a/linux/drivers/media/dvb/frontends/l64781.c
+++ b/linux/drivers/media/dvb/frontends/l64781.c
@@ -501,7 +501,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config,
{ .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } };
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct l64781_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/lgs8gl5.c b/linux/drivers/media/dvb/frontends/lgs8gl5.c
index 380ae1868..8780905bc 100644
--- a/linux/drivers/media/dvb/frontends/lgs8gl5.c
+++ b/linux/drivers/media/dvb/frontends/lgs8gl5.c
@@ -411,7 +411,7 @@ lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c)
dprintk("%s\n", __func__);
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/mt312.c b/linux/drivers/media/dvb/frontends/mt312.c
index f348f8bea..4d14f084d 100644
--- a/linux/drivers/media/dvb/frontends/mt312.c
+++ b/linux/drivers/media/dvb/frontends/mt312.c
@@ -784,7 +784,7 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config,
struct mt312_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/nxt6000.c b/linux/drivers/media/dvb/frontends/nxt6000.c
index 0eef22dbf..a763ec756 100644
--- a/linux/drivers/media/dvb/frontends/nxt6000.c
+++ b/linux/drivers/media/dvb/frontends/nxt6000.c
@@ -545,7 +545,7 @@ struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
struct nxt6000_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/or51132.c b/linux/drivers/media/dvb/frontends/or51132.c
index 8133ea3cd..38e67accb 100644
--- a/linux/drivers/media/dvb/frontends/or51132.c
+++ b/linux/drivers/media/dvb/frontends/or51132.c
@@ -562,7 +562,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config,
struct or51132_state* state = NULL;
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/or51211.c b/linux/drivers/media/dvb/frontends/or51211.c
index 16cf2fdd5..c709ce677 100644
--- a/linux/drivers/media/dvb/frontends/or51211.c
+++ b/linux/drivers/media/dvb/frontends/or51211.c
@@ -527,7 +527,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config,
struct or51211_state* state = NULL;
/* Allocate memory for the internal state */
- state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL);
if (state == NULL)
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/s5h1409.c b/linux/drivers/media/dvb/frontends/s5h1409.c
index 3e08d985d..fb3011518 100644
--- a/linux/drivers/media/dvb/frontends/s5h1409.c
+++ b/linux/drivers/media/dvb/frontends/s5h1409.c
@@ -796,7 +796,7 @@ struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config,
u16 reg;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/s5h1411.c b/linux/drivers/media/dvb/frontends/s5h1411.c
index 66e2dd6d6..d8adf1e32 100644
--- a/linux/drivers/media/dvb/frontends/s5h1411.c
+++ b/linux/drivers/media/dvb/frontends/s5h1411.c
@@ -844,7 +844,7 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config,
u16 reg;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct s5h1411_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/si21xx.c b/linux/drivers/media/dvb/frontends/si21xx.c
index 216a532ab..ee0a0a2dd 100644
--- a/linux/drivers/media/dvb/frontends/si21xx.c
+++ b/linux/drivers/media/dvb/frontends/si21xx.c
@@ -1003,7 +1003,7 @@ struct dvb_frontend *si21xx_attach(const struct si21xx_config *config,
dprintk("%s\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/sp8870.c b/linux/drivers/media/dvb/frontends/sp8870.c
index 1c9a9b405..b85eb60a8 100644
--- a/linux/drivers/media/dvb/frontends/sp8870.c
+++ b/linux/drivers/media/dvb/frontends/sp8870.c
@@ -557,7 +557,7 @@ struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
struct sp8870_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct sp8870_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/sp887x.c b/linux/drivers/media/dvb/frontends/sp887x.c
index 559509ab4..4a7c3d842 100644
--- a/linux/drivers/media/dvb/frontends/sp887x.c
+++ b/linux/drivers/media/dvb/frontends/sp887x.c
@@ -557,7 +557,7 @@ struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
struct sp887x_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/stb6100.c b/linux/drivers/media/dvb/frontends/stb6100.c
index 2b1af36b8..2de9e0c01 100644
--- a/linux/drivers/media/dvb/frontends/stb6100.c
+++ b/linux/drivers/media/dvb/frontends/stb6100.c
@@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/string.h>
+#include "compat.h"
#include "dvb_frontend.h"
#include "stb6100.h"
@@ -374,7 +375,9 @@ static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
/* N(I) = floor(f(VCO) / (f(XTAL) * (PSD2 ? 2 : 1))) */
nint = fvco / (state->reference << psd2);
/* N(F) = round(f(VCO) / f(XTAL) * (PSD2 ? 2 : 1) - N(I)) * 2 ^ 9 */
- nfrac = (((fvco - (nint * state->reference << psd2)) << (9 - psd2)) + state->reference / 2) / state->reference;
+ nfrac = DIV_ROUND_CLOSEST((fvco - (nint * state->reference << psd2))
+ << (9 - psd2),
+ state->reference);
dprintk(verbose, FE_DEBUG, 1,
"frequency = %u, srate = %u, g = %u, odiv = %u, psd2 = %u, fxtal = %u, osm = %u, fvco = %u, N(I) = %u, N(F) = %u",
frequency, srate, (unsigned int)g, (unsigned int)odiv,
diff --git a/linux/drivers/media/dvb/frontends/stv0288.c b/linux/drivers/media/dvb/frontends/stv0288.c
index ff1194de3..2930a5d67 100644
--- a/linux/drivers/media/dvb/frontends/stv0288.c
+++ b/linux/drivers/media/dvb/frontends/stv0288.c
@@ -570,7 +570,7 @@ struct dvb_frontend *stv0288_attach(const struct stv0288_config *config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/stv0297.c b/linux/drivers/media/dvb/frontends/stv0297.c
index 1c5a0ae82..2e709a7a5 100644
--- a/linux/drivers/media/dvb/frontends/stv0297.c
+++ b/linux/drivers/media/dvb/frontends/stv0297.c
@@ -663,7 +663,7 @@ struct dvb_frontend *stv0297_attach(const struct stv0297_config *config,
struct stv0297_state *state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0297_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/stv0299.c b/linux/drivers/media/dvb/frontends/stv0299.c
index 6c1cb1973..968874469 100644
--- a/linux/drivers/media/dvb/frontends/stv0299.c
+++ b/linux/drivers/media/dvb/frontends/stv0299.c
@@ -667,7 +667,7 @@ struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/tda10021.c b/linux/drivers/media/dvb/frontends/tda10021.c
index c6f90f33a..f410fd5e2 100644
--- a/linux/drivers/media/dvb/frontends/tda10021.c
+++ b/linux/drivers/media/dvb/frontends/tda10021.c
@@ -29,6 +29,7 @@
#include <linux/string.h>
#include <linux/slab.h>
+#include "compat.h"
#include "dvb_frontend.h"
#include "tda1002x.h"
@@ -176,7 +177,7 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate
tmp = ((symbolrate << 4) % FIN) << 8;
ratio = (ratio << 8) + tmp / FIN;
tmp = (tmp % FIN) << 8;
- ratio = (ratio << 8) + (tmp + FIN/2) / FIN;
+ ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, FIN);
BDR = ratio;
BDRI = (((XIN << 5) / symbolrate) + 1) / 2;
@@ -413,7 +414,7 @@ struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config,
u8 id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/tda10048.c b/linux/drivers/media/dvb/frontends/tda10048.c
index edf5a19a3..fd8c4cf00 100644
--- a/linux/drivers/media/dvb/frontends/tda10048.c
+++ b/linux/drivers/media/dvb/frontends/tda10048.c
@@ -1100,7 +1100,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config,
dprintk(1, "%s()\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/tda1004x.c b/linux/drivers/media/dvb/frontends/tda1004x.c
index 4981cef8b..f2a8abe0a 100644
--- a/linux/drivers/media/dvb/frontends/tda1004x.c
+++ b/linux/drivers/media/dvb/frontends/tda1004x.c
@@ -1269,7 +1269,7 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
if (!state) {
printk(KERN_ERR "Can't alocate memory for tda10045 state\n");
return NULL;
@@ -1339,7 +1339,7 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
int id;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL);
if (!state) {
printk(KERN_ERR "Can't alocate memory for tda10046 state\n");
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/tda10086.c b/linux/drivers/media/dvb/frontends/tda10086.c
index c749f96c2..502ce67cf 100644
--- a/linux/drivers/media/dvb/frontends/tda10086.c
+++ b/linux/drivers/media/dvb/frontends/tda10086.c
@@ -746,7 +746,7 @@ struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
dprintk ("%s\n", __func__);
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL);
if (!state)
return NULL;
diff --git a/linux/drivers/media/dvb/frontends/tda8083.c b/linux/drivers/media/dvb/frontends/tda8083.c
index 5b843b2e6..9369f7442 100644
--- a/linux/drivers/media/dvb/frontends/tda8083.c
+++ b/linux/drivers/media/dvb/frontends/tda8083.c
@@ -417,7 +417,7 @@ struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
struct tda8083_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda8083_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/tda80xx.c b/linux/drivers/media/dvb/frontends/tda80xx.c
index 83fc57a22..e04fd984f 100644
--- a/linux/drivers/media/dvb/frontends/tda80xx.c
+++ b/linux/drivers/media/dvb/frontends/tda80xx.c
@@ -646,7 +646,7 @@ struct dvb_frontend* tda80xx_attach(const struct tda80xx_config* config,
int ret;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct tda80xx_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/tda8261.c b/linux/drivers/media/dvb/frontends/tda8261.c
index b6d177799..320c3c36d 100644
--- a/linux/drivers/media/dvb/frontends/tda8261.c
+++ b/linux/drivers/media/dvb/frontends/tda8261.c
@@ -136,9 +136,9 @@ static int tda8261_set_state(struct dvb_frontend *fe,
if (frequency < 1450000)
buf[3] = 0x00;
- if (frequency < 2000000)
+ else if (frequency < 2000000)
buf[3] = 0x40;
- if (frequency < 2150000)
+ else if (frequency < 2150000)
buf[3] = 0x80;
/* Set params */
diff --git a/linux/drivers/media/dvb/frontends/ves1820.c b/linux/drivers/media/dvb/frontends/ves1820.c
index a184597f1..afd7f09ac 100644
--- a/linux/drivers/media/dvb/frontends/ves1820.c
+++ b/linux/drivers/media/dvb/frontends/ves1820.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <asm/div64.h>
+#include "compat.h"
#include "dvb_frontend.h"
#include "ves1820.h"
@@ -165,7 +166,7 @@ static int ves1820_set_symbolrate(struct ves1820_state *state, u32 symbolrate)
tmp = ((symbolrate << 4) % fin) << 8;
ratio = (ratio << 8) + tmp / fin;
tmp = (tmp % fin) << 8;
- ratio = (ratio << 8) + (tmp + fin / 2) / fin;
+ ratio = (ratio << 8) + DIV_ROUND_CLOSEST(tmp, fin);
BDR = ratio;
BDRI = (((state->config->xin << 5) / symbolrate) + 1) / 2;
@@ -374,7 +375,7 @@ struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
struct ves1820_state* state = NULL;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL);
if (state == NULL)
goto error;
diff --git a/linux/drivers/media/dvb/frontends/ves1x93.c b/linux/drivers/media/dvb/frontends/ves1x93.c
index bd558960b..8d7854c2f 100644
--- a/linux/drivers/media/dvb/frontends/ves1x93.c
+++ b/linux/drivers/media/dvb/frontends/ves1x93.c
@@ -456,7 +456,7 @@ struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
u8 identity;
/* allocate memory for the internal state */
- state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL);
if (state == NULL) goto error;
/* setup the state */
diff --git a/linux/drivers/media/dvb/frontends/zl10039.c b/linux/drivers/media/dvb/frontends/zl10039.c
new file mode 100644
index 000000000..11b29cb88
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/zl10039.c
@@ -0,0 +1,308 @@
+/*
+ * Driver for Zarlink ZL10039 DVB-S tuner
+ *
+ * Copyright 2007 Jan D. Louw <jd.louw@mweb.co.za>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/dvb/frontend.h>
+
+#include "dvb_frontend.h"
+#include "zl10039.h"
+
+static int debug;
+
+#define dprintk(args...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG args); \
+ } while (0)
+
+enum zl10039_model_id {
+ ID_ZL10039 = 1
+};
+
+struct zl10039_state {
+ struct i2c_adapter *i2c;
+ u8 i2c_addr;
+ u8 id;
+};
+
+enum zl10039_reg_addr {
+ PLL0 = 0,
+ PLL1,
+ PLL2,
+ PLL3,
+ RFFE,
+ BASE0,
+ BASE1,
+ BASE2,
+ LO0,
+ LO1,
+ LO2,
+ LO3,
+ LO4,
+ LO5,
+ LO6,
+ GENERAL
+};
+
+static int zl10039_read(const struct zl10039_state *state,
+ const enum zl10039_reg_addr reg, u8 *buf,
+ const size_t count)
+{
+ u8 regbuf[] = { reg };
+ struct i2c_msg msg[] = {
+ {/* Write register address */
+ .addr = state->i2c_addr,
+ .flags = 0,
+ .buf = regbuf,
+ .len = 1,
+ }, {/* Read count bytes */
+ .addr = state->i2c_addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = count,
+ },
+ };
+
+ dprintk("%s\n", __func__);
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2) {
+ dprintk("%s: i2c read error\n", __func__);
+ return -EREMOTEIO;
+ }
+
+ return 0; /* Success */
+}
+
+static int zl10039_write(struct zl10039_state *state,
+ const enum zl10039_reg_addr reg, const u8 *src,
+ const size_t count)
+{
+ u8 buf[count + 1];
+ struct i2c_msg msg = {
+ .addr = state->i2c_addr,
+ .flags = 0,
+ .buf = buf,
+ .len = count + 1,
+ };
+
+ dprintk("%s\n", __func__);
+ /* Write register address and data in one go */
+ buf[0] = reg;
+ memcpy(&buf[1], src, count);
+ if (i2c_transfer(state->i2c, &msg, 1) != 1) {
+ dprintk("%s: i2c write error\n", __func__);
+ return -EREMOTEIO;
+ }
+
+ return 0; /* Success */
+}
+
+static inline int zl10039_readreg(struct zl10039_state *state,
+ const enum zl10039_reg_addr reg, u8 *val)
+{
+ return zl10039_read(state, reg, val, 1);
+}
+
+static inline int zl10039_writereg(struct zl10039_state *state,
+ const enum zl10039_reg_addr reg,
+ const u8 val)
+{
+ return zl10039_write(state, reg, &val, 1);
+}
+
+static int zl10039_init(struct dvb_frontend *fe)
+{
+ struct zl10039_state *state = fe->tuner_priv;
+ int ret;
+
+ dprintk("%s\n", __func__);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ /* Reset logic */
+ ret = zl10039_writereg(state, GENERAL, 0x40);
+ if (ret < 0) {
+ dprintk("Note: i2c write error normal when resetting the "
+ "tuner\n");
+ }
+ /* Wake up */
+ ret = zl10039_writereg(state, GENERAL, 0x01);
+ if (ret < 0) {
+ dprintk("Tuner power up failed\n");
+ return ret;
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int zl10039_sleep(struct dvb_frontend *fe)
+{
+ struct zl10039_state *state = fe->tuner_priv;
+ int ret;
+
+ dprintk("%s\n", __func__);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = zl10039_writereg(state, GENERAL, 0x80);
+ if (ret < 0) {
+ dprintk("Tuner sleep failed\n");
+ return ret;
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int zl10039_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct zl10039_state *state = fe->tuner_priv;
+ u8 buf[6];
+ u8 bf;
+ u32 fbw;
+ u32 div;
+ int ret;
+
+ dprintk("%s\n", __func__);
+ dprintk("Set frequency = %d, symbol rate = %d\n",
+ params->frequency, params->u.qpsk.symbol_rate);
+
+ /* Assumed 10.111 MHz crystal oscillator */
+ /* Cancelled num/den 80 to prevent overflow */
+ div = (params->frequency * 1000) / 126387;
+ fbw = (params->u.qpsk.symbol_rate * 27) / 32000;
+ /* Cancelled num/den 10 to prevent overflow */
+ bf = ((fbw * 5088) / 1011100) - 1;
+
+ /*PLL divider*/
+ buf[0] = (div >> 8) & 0x7f;
+ buf[1] = (div >> 0) & 0xff;
+ /*Reference divider*/
+ /* Select reference ratio of 80 */
+ buf[2] = 0x1D;
+ /*PLL test modes*/
+ buf[3] = 0x40;
+ /*RF Control register*/
+ buf[4] = 0x6E; /* Bypass enable */
+ /*Baseband filter cutoff */
+ buf[5] = bf;
+
+ /* Open i2c gate */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ /* BR = 10, Enable filter adjustment */
+ ret = zl10039_writereg(state, BASE1, 0x0A);
+ if (ret < 0)
+ goto error;
+ /* Write new config values */
+ ret = zl10039_write(state, PLL0, buf, sizeof(buf));
+ if (ret < 0)
+ goto error;
+ /* BR = 10, Disable filter adjustment */
+ ret = zl10039_writereg(state, BASE1, 0x6A);
+ if (ret < 0)
+ goto error;
+
+ /* Close i2c gate */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ return 0;
+error:
+ dprintk("Error setting tuner\n");
+ return ret;
+}
+
+static int zl10039_release(struct dvb_frontend *fe)
+{
+ struct zl10039_state *state = fe->tuner_priv;
+
+ dprintk("%s\n", __func__);
+ kfree(state);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static struct dvb_tuner_ops zl10039_ops = {
+ .release = zl10039_release,
+ .init = zl10039_init,
+ .sleep = zl10039_sleep,
+ .set_params = zl10039_set_params,
+};
+
+struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
+ u8 i2c_addr, struct i2c_adapter *i2c)
+{
+ struct zl10039_state *state = NULL;
+
+ dprintk("%s\n", __func__);
+ state = kmalloc(sizeof(struct zl10039_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ state->i2c = i2c;
+ state->i2c_addr = i2c_addr;
+
+ /* Open i2c gate */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ /* check if this is a valid tuner */
+ if (zl10039_readreg(state, GENERAL, &state->id) < 0) {
+ /* Close i2c gate */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ goto error;
+ }
+ /* Close i2c gate */
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ state->id = state->id & 0x0f;
+ switch (state->id) {
+ case ID_ZL10039:
+ strcpy(fe->ops.tuner_ops.info.name,
+ "Zarlink ZL10039 DVB-S tuner");
+ break;
+ default:
+ dprintk("Chip ID=%x does not match a known type\n", state->id);
+ break;
+ goto error;
+ }
+
+ memcpy(&fe->ops.tuner_ops, &zl10039_ops, sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = state;
+ dprintk("Tuner attached @ i2c address 0x%02x\n", i2c_addr);
+ return fe;
+error:
+ kfree(state);
+ return NULL;
+}
+EXPORT_SYMBOL(zl10039_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+MODULE_DESCRIPTION("Zarlink ZL10039 DVB-S tuner driver");
+MODULE_AUTHOR("Jan D. Louw <jd.louw@mweb.co.za>");
+MODULE_LICENSE("GPL");
diff --git a/linux/drivers/media/dvb/frontends/zl10039.h b/linux/drivers/media/dvb/frontends/zl10039.h
new file mode 100644
index 000000000..5eee7ea16
--- /dev/null
+++ b/linux/drivers/media/dvb/frontends/zl10039.h
@@ -0,0 +1,40 @@
+/*
+ Driver for Zarlink ZL10039 DVB-S tuner
+
+ Copyright (C) 2007 Jan D. Louw <jd.louw@mweb.co.za>
+
+ 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 ZL10039_H
+#define ZL10039_H
+
+#if defined(CONFIG_DVB_ZL10039) || (defined(CONFIG_DVB_ZL10039_MODULE) \
+ && defined(MODULE))
+struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
+ u8 i2c_addr,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
+ u8 i2c_addr,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_ZL10039 */
+
+#endif /* ZL10039_H */
diff --git a/linux/drivers/media/dvb/frontends/zl10353.c b/linux/drivers/media/dvb/frontends/zl10353.c
index a69e9602c..37a050efd 100644
--- a/linux/drivers/media/dvb/frontends/zl10353.c
+++ b/linux/drivers/media/dvb/frontends/zl10353.c
@@ -39,6 +39,8 @@ struct zl10353_state {
struct zl10353_config config;
enum fe_bandwidth bandwidth;
+ u32 ucblocks;
+ u32 frequency;
};
static int debug;
@@ -204,6 +206,8 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
u16 tps = 0;
struct dvb_ofdm_parameters *op = &param->u.ofdm;
+ state->frequency = param->frequency;
+
zl10353_single_write(fe, RESET, 0x80);
udelay(200);
zl10353_single_write(fe, 0xEA, 0x01);
@@ -469,7 +473,7 @@ static int zl10353_get_parameters(struct dvb_frontend *fe,
break;
}
- param->frequency = 0;
+ param->frequency = state->frequency;
op->bandwidth = state->bandwidth;
param->inversion = INVERSION_AUTO;
@@ -549,9 +553,13 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr)
static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
{
struct zl10353_state *state = fe->demodulator_priv;
+ u32 ubl = 0;
+
+ ubl = zl10353_read_register(state, RS_UBC_1) << 8 |
+ zl10353_read_register(state, RS_UBC_0);
- *ucblocks = zl10353_read_register(state, RS_UBC_1) << 8 |
- zl10353_read_register(state, RS_UBC_0);
+ state->ucblocks += ubl;
+ *ucblocks = state->ucblocks;
return 0;
}
diff --git a/linux/drivers/media/dvb/pluto2/pluto2.c b/linux/drivers/media/dvb/pluto2/pluto2.c
index 647f6290e..8b6b2c094 100644
--- a/linux/drivers/media/dvb/pluto2/pluto2.c
+++ b/linux/drivers/media/dvb/pluto2/pluto2.c
@@ -447,7 +447,7 @@ static inline u32 divide(u32 numerator, u32 denominator)
if (denominator == 0)
return ~0;
- return (numerator + denominator / 2) / denominator;
+ return DIV_ROUND_CLOSEST(numerator, denominator);
}
/* LG Innotek TDTE-E001P (Infineon TUA6034) */
diff --git a/linux/drivers/media/dvb/siano/Kconfig b/linux/drivers/media/dvb/siano/Kconfig
index dd863f261..ff297ceaf 100644
--- a/linux/drivers/media/dvb/siano/Kconfig
+++ b/linux/drivers/media/dvb/siano/Kconfig
@@ -2,25 +2,34 @@
# Siano Mobile Silicon Digital TV device configuration
#
-config DVB_SIANO_SMS1XXX
- tristate "Siano SMS1XXX USB dongle support"
- depends on DVB_CORE && USB
+config SMS_SIANO_MDTV
+ tristate "Siano SMS1xxx based MDTV receiver"
+ depends on DVB_CORE && INPUT
---help---
- Choose Y here if you have a USB dongle with a SMS1XXX chipset.
+ Choose Y or M here if you have MDTV receiver with a Siano chipset.
- To compile this driver as a module, choose M here: the
- module will be called sms1xxx.
+ To compile this driver as a module, choose M here
+ (The module will be called smsmdtv).
-config DVB_SIANO_SMS1XXX_SMS_IDS
- bool "Enable support for Siano Mobile Silicon default USB IDs"
- depends on DVB_SIANO_SMS1XXX
- default y
- ---help---
- Choose Y here if you have a USB dongle with a SMS1XXX chipset
- that uses Siano Mobile Silicon's default usb vid:pid.
+ Further documentation on this driver can be found on the WWW
+ at http://www.siano-ms.com/
+
+if SMS_SIANO_MDTV
+menu "Siano module components"
- Choose N here if you would prefer to use Siano's external driver.
+# Hardware interfaces support
- Further documentation on this driver can be found on the WWW at
- <http://www.siano-ms.com/>.
+config SMS_USB_DRV
+ tristate "USB interface support"
+ depends on DVB_CORE && USB
+ ---help---
+ Choose if you would like to have Siano's support for USB interface
+config SMS_SDIO_DRV
+ tristate "SDIO interface support"
+ depends on DVB_CORE && MMC
+ default m
+ ---help---
+ Choose if you would like to have Siano's support for SDIO interface
+endmenu
+endif # SMS_SIANO_MDTV
diff --git a/linux/drivers/media/dvb/siano/Makefile b/linux/drivers/media/dvb/siano/Makefile
index c6644d909..c54140b5a 100644
--- a/linux/drivers/media/dvb/siano/Makefile
+++ b/linux/drivers/media/dvb/siano/Makefile
@@ -1,8 +1,9 @@
-sms1xxx-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o
-obj-$(CONFIG_DVB_SIANO_SMS1XXX) += sms1xxx.o
-obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsusb.o
-obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsdvb.o
+smsmdtv-objs := smscoreapi.o sms-cards.o smsendian.o smsir.o
+
+obj-$(CONFIG_SMS_SIANO_MDTV) += smsmdtv.o smsdvb.o
+obj-$(CONFIG_SMS_USB_DRV) += smsusb.o
+obj-$(CONFIG_SMS_SDIO_DRV) += smssdio.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
diff --git a/linux/drivers/media/dvb/siano/smscoreapi.c b/linux/drivers/media/dvb/siano/smscoreapi.c
index 54f59f24e..c0306af09 100644
--- a/linux/drivers/media/dvb/siano/smscoreapi.c
+++ b/linux/drivers/media/dvb/siano/smscoreapi.c
@@ -816,7 +816,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode)
sms_debug("set device mode to %d", mode);
if (coredev->device_flags & SMS_DEVICE_FAMILY2) {
- if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) {
+ if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) {
sms_err("invalid mode specified %d", mode);
return -EINVAL;
}
diff --git a/linux/drivers/media/dvb/siano/smsdvb.c b/linux/drivers/media/dvb/siano/smsdvb.c
index f8856d9c8..67634899d 100644
--- a/linux/drivers/media/dvb/siano/smsdvb.c
+++ b/linux/drivers/media/dvb/siano/smsdvb.c
@@ -325,6 +325,16 @@ static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client,
0 : -ETIME;
}
+static inline int led_feedback(struct smsdvb_client_t *client)
+{
+ if (client->fe_status & FE_HAS_LOCK)
+ return sms_board_led_feedback(client->coredev,
+ (client->sms_stat_dvb.ReceptionData.BER
+ == 0) ? SMS_LED_HI : SMS_LED_LO);
+ else
+ return sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+}
+
static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
{
struct smsdvb_client_t *client;
@@ -332,6 +342,8 @@ static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat)
*stat = client->fe_status;
+ led_feedback(client);
+
return 0;
}
@@ -342,6 +354,8 @@ static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber)
*ber = client->sms_stat_dvb.ReceptionData.BER;
+ led_feedback(client);
+
return 0;
}
@@ -359,6 +373,8 @@ static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
(client->sms_stat_dvb.ReceptionData.InBandPwr
+ 95) * 3 / 2;
+ led_feedback(client);
+
return 0;
}
@@ -369,6 +385,8 @@ static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr)
*snr = client->sms_stat_dvb.ReceptionData.SNR;
+ led_feedback(client);
+
return 0;
}
@@ -379,6 +397,8 @@ static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
*ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets;
+ led_feedback(client);
+
return 0;
}
@@ -404,6 +424,8 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
u32 Data[3];
} Msg;
+ int ret;
+
client->fe_status = FE_HAS_SIGNAL;
client->event_fe_state = -1;
client->event_unc_state = -1;
@@ -429,6 +451,23 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
case BANDWIDTH_AUTO: return -EOPNOTSUPP;
default: return -EINVAL;
}
+ /* Disable LNA, if any. An error is returned if no LNA is present */
+ ret = sms_board_lna_control(client->coredev, 0);
+ if (ret == 0) {
+ fe_status_t status;
+
+ /* tune with LNA off at first */
+ ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+ &client->tune_done);
+
+ smsdvb_read_status(fe, &status);
+
+ if (status & FE_HAS_LOCK)
+ return ret;
+
+ /* previous tune didnt lock - enable LNA and tune again */
+ sms_board_lna_control(client->coredev, 1);
+ }
return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
&client->tune_done);
@@ -454,6 +493,8 @@ static int smsdvb_init(struct dvb_frontend *fe)
struct smsdvb_client_t *client =
container_of(fe, struct smsdvb_client_t, frontend);
+ sms_board_power(client->coredev, 1);
+
sms_board_dvb3_event(client, DVB3_EVENT_INIT);
return 0;
}
@@ -463,6 +504,9 @@ static int smsdvb_sleep(struct dvb_frontend *fe)
struct smsdvb_client_t *client =
container_of(fe, struct smsdvb_client_t, frontend);
+ sms_board_led_feedback(client->coredev, SMS_LED_OFF);
+ sms_board_power(client->coredev, 0);
+
sms_board_dvb3_event(client, DVB3_EVENT_SLEEP);
return 0;
diff --git a/linux/drivers/media/dvb/siano/smssdio.c b/linux/drivers/media/dvb/siano/smssdio.c
index dfaa49a53..d1d652e7f 100644
--- a/linux/drivers/media/dvb/siano/smssdio.c
+++ b/linux/drivers/media/dvb/siano/smssdio.c
@@ -46,6 +46,7 @@
#define SMSSDIO_DATA 0x00
#define SMSSDIO_INT 0x04
+#define SMSSDIO_BLOCK_SIZE 128
static const struct sdio_device_id smssdio_ids[] = {
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
@@ -85,7 +86,8 @@ static int smssdio_sendrequest(void *context, void *buffer, size_t size)
sdio_claim_host(smsdev->func);
while (size >= smsdev->func->cur_blksize) {
- ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1);
+ ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
+ buffer, smsdev->func->cur_blksize);
if (ret)
goto out;
@@ -94,8 +96,8 @@ static int smssdio_sendrequest(void *context, void *buffer, size_t size)
}
if (size) {
- ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA,
- buffer, size);
+ ret = sdio_memcpy_toio(smsdev->func, SMSSDIO_DATA,
+ buffer, size);
}
out:
@@ -125,23 +127,23 @@ static void smssdio_interrupt(struct sdio_func *func)
*/
isr = sdio_readb(func, SMSSDIO_INT, &ret);
if (ret) {
- dev_err(&smsdev->func->dev,
- "Unable to read interrupt register!\n");
+ sms_err("Unable to read interrupt register!\n");
return;
}
if (smsdev->split_cb == NULL) {
cb = smscore_getbuffer(smsdev->coredev);
if (!cb) {
- dev_err(&smsdev->func->dev,
- "Unable to allocate data buffer!\n");
+ sms_err("Unable to allocate data buffer!\n");
return;
}
- ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1);
+ ret = sdio_memcpy_fromio(smsdev->func,
+ cb->p,
+ SMSSDIO_DATA,
+ SMSSDIO_BLOCK_SIZE);
if (ret) {
- dev_err(&smsdev->func->dev,
- "Error %d reading initial block!\n", ret);
+ sms_err("Error %d reading initial block!\n", ret);
return;
}
@@ -152,7 +154,10 @@ static void smssdio_interrupt(struct sdio_func *func)
return;
}
- size = hdr->msgLength - smsdev->func->cur_blksize;
+ if (hdr->msgLength > smsdev->func->cur_blksize)
+ size = hdr->msgLength - smsdev->func->cur_blksize;
+ else
+ size = 0;
} else {
cb = smsdev->split_cb;
hdr = cb->p;
@@ -162,23 +167,24 @@ static void smssdio_interrupt(struct sdio_func *func)
smsdev->split_cb = NULL;
}
- if (hdr->msgLength > smsdev->func->cur_blksize) {
+ if (size) {
void *buffer;
- size = ALIGN(size, 128);
- buffer = cb->p + hdr->msgLength;
+ buffer = cb->p + (hdr->msgLength - size);
+ size = ALIGN(size, SMSSDIO_BLOCK_SIZE);
- BUG_ON(smsdev->func->cur_blksize != 128);
+ BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE);
/*
* First attempt to transfer all of it in one go...
*/
- ret = sdio_read_blocks(smsdev->func, buffer,
- SMSSDIO_DATA, size / 128);
+ ret = sdio_memcpy_fromio(smsdev->func,
+ buffer,
+ SMSSDIO_DATA,
+ size);
if (ret && ret != -EINVAL) {
smscore_putbuffer(smsdev->coredev, cb);
- dev_err(&smsdev->func->dev,
- "Error %d reading data from card!\n", ret);
+ sms_err("Error %d reading data from card!\n", ret);
return;
}
@@ -191,12 +197,12 @@ static void smssdio_interrupt(struct sdio_func *func)
*/
if (ret == -EINVAL) {
while (size) {
- ret = sdio_read_blocks(smsdev->func,
- buffer, SMSSDIO_DATA, 1);
+ ret = sdio_memcpy_fromio(smsdev->func,
+ buffer, SMSSDIO_DATA,
+ smsdev->func->cur_blksize);
if (ret) {
smscore_putbuffer(smsdev->coredev, cb);
- dev_err(&smsdev->func->dev,
- "Error %d reading "
+ sms_err("Error %d reading "
"data from card!\n", ret);
return;
}
@@ -269,7 +275,7 @@ static int smssdio_probe(struct sdio_func *func,
if (ret)
goto release;
- ret = sdio_set_block_size(func, 128);
+ ret = sdio_set_block_size(func, SMSSDIO_BLOCK_SIZE);
if (ret)
goto disable;
diff --git a/linux/drivers/media/radio/Kconfig b/linux/drivers/media/radio/Kconfig
index 3315cac87..0e39edf75 100644
--- a/linux/drivers/media/radio/Kconfig
+++ b/linux/drivers/media/radio/Kconfig
@@ -288,16 +288,6 @@ config RADIO_TYPHOON
To compile this driver as a module, choose M here: the
module will be called radio-typhoon.
-config RADIO_TYPHOON_PROC_FS
- bool "Support for /proc/radio-typhoon"
- depends on PROC_FS && RADIO_TYPHOON
- help
- Say Y here if you want the typhoon radio card driver to write
- status information (frequency, volume, muted, mute frequency,
- base address) to /proc/radio-typhoon. The file can be viewed with
- your favorite pager (i.e. use "more /proc/radio-typhoon" or "less
- /proc/radio-typhoon" or simply "cat /proc/radio-typhoon").
-
config RADIO_TYPHOON_PORT
hex "Typhoon I/O port (0x316 or 0x336)"
depends on RADIO_TYPHOON=y
@@ -351,29 +341,11 @@ config USB_DSBR
To compile this driver as a module, choose M here: the
module will be called dsbr100.
-config USB_SI470X
- tristate "Silicon Labs Si470x FM Radio Receiver support"
- depends on USB && VIDEO_V4L2
- ---help---
- This is a driver for USB devices with the Silicon Labs SI470x
- chip. Currently these devices are known to work:
- - 10c4:818a: Silicon Labs USB FM Radio Reference Design
- - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music)
- - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
-
- Sound is provided by the ALSA USB Audio/MIDI driver. Therefore
- if you don't want to use the device solely for RDS receiving,
- it is recommended to also select SND_USB_AUDIO.
-
- Please have a look at the documentation, especially on how
- to redirect the audio stream from the radio to your sound device:
- Documentation/video4linux/si470x.txt
-
- Say Y here if you want to connect this type of radio to your
- computer's USB port.
+config RADIO_SI470X
+ bool "Silicon Labs Si470x FM Radio Receiver support"
+ depends on VIDEO_V4L2
- To compile this driver as a module, choose M here: the
- module will be called radio-si470x.
+source "drivers/media/radio/si470x/Kconfig"
config USB_MR800
tristate "AverMedia MR 800 USB FM radio support"
diff --git a/linux/drivers/media/radio/Makefile b/linux/drivers/media/radio/Makefile
index 0f2b35b3e..18111e1fe 100644
--- a/linux/drivers/media/radio/Makefile
+++ b/linux/drivers/media/radio/Makefile
@@ -17,7 +17,7 @@ obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o
obj-$(CONFIG_RADIO_TRUST) += radio-trust.o
obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
-obj-$(CONFIG_USB_SI470X) += radio-si470x.o
+obj-$(CONFIG_RADIO_SI470X) += si470x/
obj-$(CONFIG_USB_MR800) += radio-mr800.o
obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
diff --git a/linux/drivers/media/radio/radio-si470x.c b/linux/drivers/media/radio/radio-si470x.c
deleted file mode 100644
index 5b4cab3d0..000000000
--- a/linux/drivers/media/radio/radio-si470x.c
+++ /dev/null
@@ -1,1870 +0,0 @@
-/*
- * drivers/media/radio/radio-si470x.c
- *
- * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers:
- * - Silicon Labs USB FM Radio Reference Design
- * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF)
- * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
- * - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear)
- *
- * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-
-/*
- * History:
- * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.0
- * - First working version
- * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.1
- * - Improved error handling, every function now returns errno
- * - Improved multi user access (start/mute/stop)
- * - Channel doesn't get lost anymore after start/mute/stop
- * - RDS support added (polling mode via interrupt EP 1)
- * - marked default module parameters with *value*
- * - switched from bit structs to bit masks
- * - header file cleaned and integrated
- * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.2
- * - hex values are now lower case
- * - commented USB ID for ADS/Tech moved on todo list
- * - blacklisted si470x in hid-quirks.c
- * - rds buffer handling functions integrated into *_work, *_read
- * - rds_command in si470x_poll exchanged against simple retval
- * - check for firmware version 15
- * - code order and prototypes still remain the same
- * - spacing and bottom of band codes remain the same
- * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.3
- * - code reordered to avoid function prototypes
- * - switch/case defaults are now more user-friendly
- * - unified comment style
- * - applied all checkpatch.pl v1.12 suggestions
- * except the warning about the too long lines with bit comments
- * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
- * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.4
- * - avoid poss. locking when doing copy_to_user which may sleep
- * - RDS is automatically activated on read now
- * - code cleaned of unnecessary rds_commands
- * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
- * (thanks to Guillaume RAMOUSSE)
- * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.5
- * - number of seek_retries changed to tune_timeout
- * - fixed problem with incomplete tune operations by own buffers
- * - optimization of variables and printf types
- * - improved error logging
- * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Oliver Neukum <oliver@neukum.org>
- * Version 1.0.6
- * - fixed coverity checker warnings in *_usb_driver_disconnect
- * - probe()/open() race by correct ordering in probe()
- * - DMA coherency rules by separate allocation of all buffers
- * - use of endianness macros
- * - abuse of spinlock, replaced by mutex
- * - racy handling of timer in disconnect,
- * replaced by delayed_work
- * - racy interruptible_sleep_on(),
- * replaced with wait_event_interruptible()
- * - handle signals in read()
- * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Oliver Neukum <oliver@neukum.org>
- * Version 1.0.7
- * - usb autosuspend support
- * - unplugging fixed
- * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net>
- * Version 1.0.8
- * - hardware frequency seek support
- * - afc indication
- * - more safety checks, let si470x_get_freq return errno
- * - vidioc behavior corrected according to v4l2 spec
- * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com>
- * - add support for KWorld USB FM Radio FM700
- * - blacklisted KWorld radio in hid-core.c and hid-ids.h
- * 2008-12-03 Mark Lord <mlord@pobox.com>
- * - add support for DealExtreme USB Radio
- * 2009-01-31 Bob Ross <pigiron@gmx.com>
- * - correction of stereo detection/setting
- * - correction of signal strength indicator scaling
- * 2009-01-31 Rick Bronson <rick@efn.org>
- * Tobias Lorenz <tobias.lorenz@gmx.net>
- * - add LED status output
- * - get HW/SW version from scratchpad
- * 2009-06-16 Edouard Lafargue <edouard@lafargue.name>
- * Version 1.0.10
- * - add support for interrupt mode for RDS endpoint,
- * instead of polling.
- * Improves RDS reception significantly
- *
- * ToDo:
- * - add firmware download/update support
- */
-
-
-/* driver definitions */
-#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
-#define DRIVER_NAME "radio-si470x"
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)
-#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
-#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.10"
-
-
-/* kernel includes */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include <linux/hid.h>
-#include <linux/version.h>
-#include "compat.h"
-#include <linux/videodev2.h>
-#include <linux/mutex.h>
-#include <media/v4l2-common.h>
-#include <media/v4l2-ioctl.h>
-#include <media/rds.h>
-#include <asm/unaligned.h>
-
-
-/* USB Device ID List */
-static struct usb_device_id si470x_usb_driver_id_table[] = {
- /* Silicon Labs USB FM Radio Reference Design */
- { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
- /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
- { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
- /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */
- { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },
- /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */
- { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },
- /* Terminating entry */
- { }
-};
-MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
-
-
-
-/**************************************************************************
- * Module Parameters
- **************************************************************************/
-
-/* Radio Nr */
-static int radio_nr = -1;
-module_param(radio_nr, int, 0444);
-MODULE_PARM_DESC(radio_nr, "Radio Nr");
-
-/* Spacing (kHz) */
-/* 0: 200 kHz (USA, Australia) */
-/* 1: 100 kHz (Europe, Japan) */
-/* 2: 50 kHz */
-static unsigned short space = 2;
-module_param(space, ushort, 0444);
-MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
-
-/* Bottom of Band (MHz) */
-/* 0: 87.5 - 108 MHz (USA, Europe)*/
-/* 1: 76 - 108 MHz (Japan wide band) */
-/* 2: 76 - 90 MHz (Japan) */
-static unsigned short band = 1;
-module_param(band, ushort, 0444);
-MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
-
-/* De-emphasis */
-/* 0: 75 us (USA) */
-/* 1: 50 us (Europe, Australia, Japan) */
-static unsigned short de = 1;
-module_param(de, ushort, 0444);
-MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
-
-/* USB timeout */
-static unsigned int usb_timeout = 500;
-module_param(usb_timeout, uint, 0644);
-MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
-
-/* Tune timeout */
-static unsigned int tune_timeout = 3000;
-module_param(tune_timeout, uint, 0644);
-MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
-
-/* Seek timeout */
-static unsigned int seek_timeout = 5000;
-module_param(seek_timeout, uint, 0644);
-MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
-
-/* RDS buffer blocks */
-static unsigned int rds_buf = 100;
-module_param(rds_buf, uint, 0444);
-MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
-
-/* RDS maximum block errors */
-static unsigned short max_rds_errors = 1;
-/* 0 means 0 errors requiring correction */
-/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
-/* 2 means 3-5 errors requiring correction */
-/* 3 means 6+ errors or errors in checkword, correction not possible */
-module_param(max_rds_errors, ushort, 0644);
-MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
-
-
-/**************************************************************************
- * Register Definitions
- **************************************************************************/
-#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */
-#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */
-#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */
-
-#define DEVICEID 0 /* Device ID */
-#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */
-#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */
-
-#define CHIPID 1 /* Chip ID */
-#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */
-#define CHIPID_DEV 0x0200 /* bits 09..09: Device */
-#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */
-
-#define POWERCFG 2 /* Power Configuration */
-#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */
-#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */
-#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */
-#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */
-#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */
-#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */
-#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */
-#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */
-#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */
-
-#define CHANNEL 3 /* Channel */
-#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */
-#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */
-
-#define SYSCONFIG1 4 /* System Configuration 1 */
-#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */
-#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */
-#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */
-#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */
-#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */
-#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */
-#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */
-#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */
-#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */
-
-#define SYSCONFIG2 5 /* System Configuration 2 */
-#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
-#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
-#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
-#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
-
-#define SYSCONFIG3 6 /* System Configuration 3 */
-#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */
-#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */
-#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */
-#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */
-
-#define TEST1 7 /* Test 1 */
-#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */
-
-#define TEST2 8 /* Test 2 */
-/* TEST2 only contains reserved bits */
-
-#define BOOTCONFIG 9 /* Boot Configuration */
-/* BOOTCONFIG only contains reserved bits */
-
-#define STATUSRSSI 10 /* Status RSSI */
-#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */
-#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */
-#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */
-#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */
-#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */
-#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */
-#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */
-#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */
-
-#define READCHAN 11 /* Read Channel */
-#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */
-#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */
-#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */
-#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */
-
-#define RDSA 12 /* RDSA */
-#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */
-
-#define RDSB 13 /* RDSB */
-#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */
-
-#define RDSC 14 /* RDSC */
-#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */
-
-#define RDSD 15 /* RDSD */
-#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */
-
-
-
-/**************************************************************************
- * USB HID Reports
- **************************************************************************/
-
-/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
-/* with the (REPORT_ID - 1) corresponding to the register address across USB */
-/* endpoint 0 using GET_REPORT and SET_REPORT */
-#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1)
-#define REGISTER_REPORT(reg) ((reg) + 1)
-
-/* Report 17 gives direct read/write access to the entire Si470x register */
-/* map across endpoint 0 using GET_REPORT and SET_REPORT */
-#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
-#define ENTIRE_REPORT 17
-
-/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
-/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
-#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
-#define RDS_REPORT 18
-
-/* Report 19: LED state */
-#define LED_REPORT_SIZE 3
-#define LED_REPORT 19
-
-/* Report 19: stream */
-#define STREAM_REPORT_SIZE 3
-#define STREAM_REPORT 19
-
-/* Report 20: scratch */
-#define SCRATCH_PAGE_SIZE 63
-#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1)
-#define SCRATCH_REPORT 20
-
-/* Reports 19-22: flash upgrade of the C8051F321 */
-#define WRITE_REPORT_SIZE 4
-#define WRITE_REPORT 19
-#define FLASH_REPORT_SIZE 64
-#define FLASH_REPORT 20
-#define CRC_REPORT_SIZE 3
-#define CRC_REPORT 21
-#define RESPONSE_REPORT_SIZE 2
-#define RESPONSE_REPORT 22
-
-/* Report 23: currently unused, but can accept 60 byte reports on the HID */
-/* interrupt out endpoint 2 every 1 millisecond */
-#define UNUSED_REPORT 23
-
-
-
-/**************************************************************************
- * Software/Hardware Versions
- **************************************************************************/
-#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
-#define RADIO_SW_VERSION 7
-#define RADIO_SW_VERSION_CURRENT 15
-#define RADIO_HW_VERSION 1
-
-#define SCRATCH_PAGE_SW_VERSION 1
-#define SCRATCH_PAGE_HW_VERSION 2
-
-
-
-/**************************************************************************
- * LED State Definitions
- **************************************************************************/
-#define LED_COMMAND 0x35
-
-#define NO_CHANGE_LED 0x00
-#define ALL_COLOR_LED 0x01 /* streaming state */
-#define BLINK_GREEN_LED 0x02 /* connect state */
-#define BLINK_RED_LED 0x04
-#define BLINK_ORANGE_LED 0x10 /* disconnect state */
-#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */
-#define SOLID_RED_LED 0x40 /* bootload state */
-#define SOLID_ORANGE_LED 0x80
-
-
-
-/**************************************************************************
- * Stream State Definitions
- **************************************************************************/
-#define STREAM_COMMAND 0x36
-#define STREAM_VIDPID 0x00
-#define STREAM_AUDIO 0xff
-
-
-
-/**************************************************************************
- * Bootloader / Flash Commands
- **************************************************************************/
-
-/* unique id sent to bootloader and required to put into a bootload state */
-#define UNIQUE_BL_ID 0x34
-
-/* mask for the flash data */
-#define FLASH_DATA_MASK 0x55
-
-/* bootloader commands */
-#define GET_SW_VERSION_COMMAND 0x00
-#define SET_PAGE_COMMAND 0x01
-#define ERASE_PAGE_COMMAND 0x02
-#define WRITE_PAGE_COMMAND 0x03
-#define CRC_ON_PAGE_COMMAND 0x04
-#define READ_FLASH_BYTE_COMMAND 0x05
-#define RESET_DEVICE_COMMAND 0x06
-#define GET_HW_VERSION_COMMAND 0x07
-#define BLANK 0xff
-
-/* bootloader command responses */
-#define COMMAND_OK 0x01
-#define COMMAND_FAILED 0x02
-#define COMMAND_PENDING 0x03
-
-
-
-/**************************************************************************
- * General Driver Definitions
- **************************************************************************/
-
-/*
- * si470x_device - private data
- */
-struct si470x_device {
- /* reference to USB and video device */
- struct usb_device *usbdev;
- struct usb_interface *intf;
- struct video_device *videodev;
-
- /* Interrupt endpoint handling */
- char *int_in_buffer;
- struct usb_endpoint_descriptor *int_in_endpoint;
- struct urb *int_in_urb;
- int int_in_running;
-
- /* driver management */
- unsigned int users;
- unsigned char disconnected;
- struct mutex disconnect_lock;
-
- /* Silabs internal registers (0..15) */
- unsigned short registers[RADIO_REGISTER_NUM];
-
- /* RDS receive buffer */
- wait_queue_head_t read_queue;
- struct mutex lock; /* buffer locking */
- unsigned char *buffer; /* size is always multiple of three */
- unsigned int buf_size;
- unsigned int rd_index;
- unsigned int wr_index;
-
- /* scratch page */
- unsigned char software_version;
- unsigned char hardware_version;
-};
-
-
-/*
- * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
- * 62.5 kHz otherwise.
- * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
- * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
- * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
- */
-#define FREQ_MUL (1000000 / 62.5)
-
-
-
-/**************************************************************************
- * General Driver Functions - REGISTER_REPORTs
- **************************************************************************/
-
-/*
- * si470x_get_report - receive a HID report
- */
-static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
-{
- unsigned char *report = (unsigned char *) buf;
- int retval;
-
- retval = usb_control_msg(radio->usbdev,
- usb_rcvctrlpipe(radio->usbdev, 0),
- HID_REQ_GET_REPORT,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
- report[0], 2,
- buf, size, usb_timeout);
-
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": si470x_get_report: usb_control_msg returned %d\n",
- retval);
- return retval;
-}
-
-
-/*
- * si470x_set_report - send a HID report
- */
-static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
-{
- unsigned char *report = (unsigned char *) buf;
- int retval;
-
- retval = usb_control_msg(radio->usbdev,
- usb_sndctrlpipe(radio->usbdev, 0),
- HID_REQ_SET_REPORT,
- USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
- report[0], 2,
- buf, size, usb_timeout);
-
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": si470x_set_report: usb_control_msg returned %d\n",
- retval);
- return retval;
-}
-
-
-/*
- * si470x_get_register - read register
- */
-static int si470x_get_register(struct si470x_device *radio, int regnr)
-{
- unsigned char buf[REGISTER_REPORT_SIZE];
- int retval;
-
- buf[0] = REGISTER_REPORT(regnr);
-
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
- if (retval >= 0)
- radio->registers[regnr] = get_unaligned_be16(&buf[1]);
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-/*
- * si470x_set_register - write register
- */
-static int si470x_set_register(struct si470x_device *radio, int regnr)
-{
- unsigned char buf[REGISTER_REPORT_SIZE];
- int retval;
-
- buf[0] = REGISTER_REPORT(regnr);
- put_unaligned_be16(radio->registers[regnr], &buf[1]);
-
- retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-/*
- * si470x_set_chan - set the channel
- */
-static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
-{
- int retval;
- unsigned long timeout;
- bool timed_out = 0;
-
- /* start tuning */
- radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
- radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
- retval = si470x_set_register(radio, CHANNEL);
- if (retval < 0)
- goto done;
-
- /* wait till tune operation has completed */
- timeout = jiffies + msecs_to_jiffies(tune_timeout);
- do {
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto stop;
- timed_out = time_after(jiffies, timeout);
- } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
- (!timed_out));
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n");
- if (timed_out)
- printk(KERN_WARNING DRIVER_NAME
- ": tune timed out after %u ms\n", tune_timeout);
-
-stop:
- /* stop tuning */
- radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
- retval = si470x_set_register(radio, CHANNEL);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_get_freq - get the frequency
- */
-static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
-{
- unsigned int spacing, band_bottom;
- unsigned short chan;
- int retval;
-
- /* Spacing (kHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
- /* 0: 200 kHz (USA, Australia) */
- case 0:
- spacing = 0.200 * FREQ_MUL; break;
- /* 1: 100 kHz (Europe, Japan) */
- case 1:
- spacing = 0.100 * FREQ_MUL; break;
- /* 2: 50 kHz */
- default:
- spacing = 0.050 * FREQ_MUL; break;
- };
-
- /* Bottom of Band (MHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe) */
- case 0:
- band_bottom = 87.5 * FREQ_MUL; break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- default:
- band_bottom = 76 * FREQ_MUL; break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2:
- band_bottom = 76 * FREQ_MUL; break;
- };
-
- /* read channel */
- retval = si470x_get_register(radio, READCHAN);
- chan = radio->registers[READCHAN] & READCHAN_READCHAN;
-
- /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
- *freq = chan * spacing + band_bottom;
-
- return retval;
-}
-
-
-/*
- * si470x_set_freq - set the frequency
- */
-static int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
-{
- unsigned int spacing, band_bottom;
- unsigned short chan;
-
- /* Spacing (kHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
- /* 0: 200 kHz (USA, Australia) */
- case 0:
- spacing = 0.200 * FREQ_MUL; break;
- /* 1: 100 kHz (Europe, Japan) */
- case 1:
- spacing = 0.100 * FREQ_MUL; break;
- /* 2: 50 kHz */
- default:
- spacing = 0.050 * FREQ_MUL; break;
- };
-
- /* Bottom of Band (MHz) */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe) */
- case 0:
- band_bottom = 87.5 * FREQ_MUL; break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- default:
- band_bottom = 76 * FREQ_MUL; break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2:
- band_bottom = 76 * FREQ_MUL; break;
- };
-
- /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
- chan = (freq - band_bottom) / spacing;
-
- return si470x_set_chan(radio, chan);
-}
-
-
-/*
- * si470x_set_seek - set seek
- */
-static int si470x_set_seek(struct si470x_device *radio,
- unsigned int wrap_around, unsigned int seek_upward)
-{
- int retval = 0;
- unsigned long timeout;
- bool timed_out = 0;
-
- /* start seeking */
- radio->registers[POWERCFG] |= POWERCFG_SEEK;
- if (wrap_around == 1)
- radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
- else
- radio->registers[POWERCFG] |= POWERCFG_SKMODE;
- if (seek_upward == 1)
- radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
- else
- radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
- retval = si470x_set_register(radio, POWERCFG);
- if (retval < 0)
- goto done;
-
- /* wait till seek operation has completed */
- timeout = jiffies + msecs_to_jiffies(seek_timeout);
- do {
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto stop;
- timed_out = time_after(jiffies, timeout);
- } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
- (!timed_out));
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
- printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n");
- if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
- printk(KERN_WARNING DRIVER_NAME
- ": seek failed / band limit reached\n");
- if (timed_out)
- printk(KERN_WARNING DRIVER_NAME
- ": 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;
-
- return retval;
-}
-
-
-/*
- * si470x_start - switch on radio
- */
-static int si470x_start(struct si470x_device *radio)
-{
- int retval;
-
- /* powercfg */
- radio->registers[POWERCFG] =
- POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
- retval = si470x_set_register(radio, POWERCFG);
- if (retval < 0)
- goto done;
-
- /* sysconfig 1 */
- radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
- retval = si470x_set_register(radio, SYSCONFIG1);
- if (retval < 0)
- goto done;
-
- /* sysconfig 2 */
- radio->registers[SYSCONFIG2] =
- (0x3f << 8) | /* SEEKTH */
- ((band << 6) & SYSCONFIG2_BAND) | /* BAND */
- ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
- 15; /* VOLUME (max) */
- retval = si470x_set_register(radio, SYSCONFIG2);
- if (retval < 0)
- goto done;
-
- /* reset last channel */
- retval = si470x_set_chan(radio,
- radio->registers[CHANNEL] & CHANNEL_CHAN);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_stop - switch off radio
- */
-static int si470x_stop(struct si470x_device *radio)
-{
- int retval;
-
- /* sysconfig 1 */
- radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
- retval = si470x_set_register(radio, SYSCONFIG1);
- if (retval < 0)
- goto done;
-
- /* powercfg */
- radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
- /* POWERCFG_ENABLE has to automatically go low */
- radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
- retval = si470x_set_register(radio, POWERCFG);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_rds_on - switch on rds reception
- */
-static int si470x_rds_on(struct si470x_device *radio)
-{
- int retval;
-
- /* sysconfig 1 */
- mutex_lock(&radio->lock);
- radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
- retval = si470x_set_register(radio, SYSCONFIG1);
- if (retval < 0)
- radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
- mutex_unlock(&radio->lock);
-
- return retval;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - ENTIRE_REPORT
- **************************************************************************/
-
-/*
- * si470x_get_all_registers - read entire registers
- */
-static int si470x_get_all_registers(struct si470x_device *radio)
-{
- unsigned char buf[ENTIRE_REPORT_SIZE];
- int retval;
- unsigned char regnr;
-
- buf[0] = ENTIRE_REPORT;
-
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
- if (retval >= 0)
- for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
- radio->registers[regnr] = get_unaligned_be16(
- &buf[regnr * RADIO_REGISTER_SIZE + 1]);
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - LED_REPORT
- **************************************************************************/
-
-/*
- * si470x_set_led_state - sets the led state
- */
-static int si470x_set_led_state(struct si470x_device *radio,
- unsigned char led_state)
-{
- unsigned char buf[LED_REPORT_SIZE];
- int retval;
-
- buf[0] = LED_REPORT;
- buf[1] = LED_COMMAND;
- buf[2] = led_state;
-
- retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * General Driver Functions - SCRATCH_REPORT
- **************************************************************************/
-
-/*
- * si470x_get_scratch_versions - gets the scratch page and version infos
- */
-static int si470x_get_scratch_page_versions(struct si470x_device *radio)
-{
- unsigned char buf[SCRATCH_REPORT_SIZE];
- int retval;
-
- buf[0] = SCRATCH_REPORT;
-
- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
-
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: "
- "si470x_get_report returned %d\n", retval);
- else {
- radio->software_version = buf[1];
- radio->hardware_version = buf[2];
- }
-
- return (retval < 0) ? -EINVAL : 0;
-}
-
-
-
-/**************************************************************************
- * RDS Driver Functions
- **************************************************************************/
-
-/*
- * si470x_int_in_callback - rds callback and processing function
- *
- * TODO: do we need to use mutex locks in some sections?
- */
-static void si470x_int_in_callback(struct urb *urb)
-{
- struct si470x_device *radio = urb->context;
- unsigned char buf[RDS_REPORT_SIZE];
- int retval;
- unsigned char regnr;
- unsigned char blocknum;
- unsigned short bler; /* rds block errors */
- unsigned short rds;
- unsigned char tmpbuf[3];
-
- if (urb->status) {
- if (urb->status == -ENOENT ||
- urb->status == -ECONNRESET ||
- urb->status == -ESHUTDOWN) {
- return;
- } else {
- printk(KERN_WARNING DRIVER_NAME
- ": non-zero urb status (%d)\n", urb->status);
- goto resubmit; /* Maybe we can recover. */
- }
- }
-
- /* safety checks */
- if (radio->disconnected)
- return;
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
- goto resubmit;
-
- if (urb->actual_length > 0) {
- /* Update RDS registers with URB data */
- buf[0] = RDS_REPORT;
- for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
- radio->registers[STATUSRSSI + regnr] =
- get_unaligned_be16(&radio->int_in_buffer[
- regnr * RADIO_REGISTER_SIZE + 1]);
- /* get rds blocks */
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
- /* No RDS group ready, better luck next time */
- goto resubmit;
- }
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
- /* RDS decoder not synchronized */
- goto resubmit;
- }
- for (blocknum = 0; blocknum < 4; blocknum++) {
- switch (blocknum) {
- default:
- bler = (radio->registers[STATUSRSSI] &
- STATUSRSSI_BLERA) >> 9;
- rds = radio->registers[RDSA];
- break;
- case 1:
- bler = (radio->registers[READCHAN] &
- READCHAN_BLERB) >> 14;
- rds = radio->registers[RDSB];
- break;
- case 2:
- bler = (radio->registers[READCHAN] &
- READCHAN_BLERC) >> 12;
- rds = radio->registers[RDSC];
- break;
- case 3:
- bler = (radio->registers[READCHAN] &
- READCHAN_BLERD) >> 10;
- rds = radio->registers[RDSD];
- break;
- };
-
- /* Fill the V4L2 RDS buffer */
- put_unaligned_le16(rds, &tmpbuf);
- tmpbuf[2] = blocknum; /* offset name */
- tmpbuf[2] |= blocknum << 3; /* received offset */
- if (bler > max_rds_errors)
- tmpbuf[2] |= 0x80; /* uncorrectable errors */
- else if (bler > 0)
- tmpbuf[2] |= 0x40; /* corrected error(s) */
-
- /* copy RDS block to internal buffer */
- memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
- radio->wr_index += 3;
-
- /* wrap write pointer */
- if (radio->wr_index >= radio->buf_size)
- radio->wr_index = 0;
-
- /* check for overflow */
- if (radio->wr_index == radio->rd_index) {
- /* increment and wrap read pointer */
- radio->rd_index += 3;
- if (radio->rd_index >= radio->buf_size)
- radio->rd_index = 0;
- }
- }
- if (radio->wr_index != radio->rd_index)
- wake_up_interruptible(&radio->read_queue);
- }
-
-resubmit:
- /* Resubmit if we're still running. */
- if (radio->int_in_running && radio->usbdev) {
- retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC);
- if (retval) {
- printk(KERN_WARNING DRIVER_NAME
- ": resubmitting urb failed (%d)", retval);
- radio->int_in_running = 0;
- }
- }
-}
-
-
-
-/**************************************************************************
- * File Operations Interface
- **************************************************************************/
-
-/*
- * si470x_fops_read - read RDS data
- */
-static ssize_t si470x_fops_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
- unsigned int block_count = 0;
-
- /* switch on rds reception */
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
- si470x_rds_on(radio);
- }
-
- /* block if no new data available */
- while (radio->wr_index == radio->rd_index) {
- if (file->f_flags & O_NONBLOCK) {
- retval = -EWOULDBLOCK;
- goto done;
- }
- if (wait_event_interruptible(radio->read_queue,
- radio->wr_index != radio->rd_index) < 0) {
- retval = -EINTR;
- goto done;
- }
- }
-
- /* calculate block count from byte count */
- count /= 3;
-
- /* copy RDS block out of internal buffer and to user buffer */
- mutex_lock(&radio->lock);
- while (block_count < count) {
- if (radio->rd_index == radio->wr_index)
- break;
-
- /* always transfer rds complete blocks */
- if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
- /* retval = -EFAULT; */
- break;
-
- /* increment and wrap read pointer */
- radio->rd_index += 3;
- if (radio->rd_index >= radio->buf_size)
- radio->rd_index = 0;
-
- /* increment counters */
- block_count++;
- buf += 3;
- retval += 3;
- }
- mutex_unlock(&radio->lock);
-
-done:
- return retval;
-}
-
-
-/*
- * si470x_fops_poll - poll RDS data
- */
-static unsigned int si470x_fops_poll(struct file *file,
- struct poll_table_struct *pts)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* switch on rds reception */
- if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
- si470x_rds_on(radio);
- }
-
- poll_wait(file, &radio->read_queue, pts);
-
- if (radio->rd_index != radio->wr_index)
- retval = POLLIN | POLLRDNORM;
-
- return retval;
-}
-
-
-/*
- * si470x_fops_open - file open
- */
-static int si470x_fops_open(struct file *file)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval;
-
- lock_kernel();
- 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) {
- printk(KERN_INFO DRIVER_NAME
- ": submitting int urb failed (%d)\n", retval);
- radio->int_in_running = 0;
- usb_autopm_put_interface(radio->intf);
- }
- }
-
-done:
- unlock_kernel();
- return retval;
-}
-
-
-/*
- * si470x_fops_release - file release
- */
-static 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->disconnect_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);
- kfree(radio);
- goto unlock;
- }
-
- /* cancel read processes */
- wake_up_interruptible(&radio->read_queue);
-
- /* stop radio */
- retval = si470x_stop(radio);
- usb_autopm_put_interface(radio->intf);
- }
-unlock:
- mutex_unlock(&radio->disconnect_lock);
-done:
- return retval;
-}
-
-
-/*
- * si470x_fops - file operations interface
- */
-static const struct v4l2_file_operations si470x_fops = {
- .owner = THIS_MODULE,
- .read = si470x_fops_read,
- .poll = si470x_fops_poll,
- .ioctl = video_ioctl2,
- .open = si470x_fops_open,
- .release = si470x_fops_release,
-};
-
-
-
-/**************************************************************************
- * Video4Linux Interface
- **************************************************************************/
-
-/*
- * si470x_vidioc_querycap - query device capabilities
- */
-static int si470x_vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *capability)
-{
- struct si470x_device *radio = video_drvdata(file);
-
- strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
- strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
- usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info));
- capability->version = DRIVER_KERNEL_VERSION;
- capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
- V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
-
- return 0;
-}
-
-
-/*
- * si470x_vidioc_queryctrl - enumerate control items
- */
-static int si470x_vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- 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)
- printk(KERN_WARNING DRIVER_NAME
- ": 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;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- 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)
- printk(KERN_WARNING DRIVER_NAME
- ": get control failed with %d\n", retval);
- 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)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = 0;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
-
- 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;
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value == 1)
- radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
- else
- radio->registers[POWERCFG] |= POWERCFG_DMUTE;
- retval = si470x_set_register(radio, POWERCFG);
- break;
- default:
- retval = -EINVAL;
- }
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set control failed with %d\n", retval);
- 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;
-}
-
-
-/*
- * si470x_vidioc_g_tuner - get tuner attributes
- */
-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;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (tuner->index != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- retval = si470x_get_register(radio, STATUSRSSI);
- if (retval < 0)
- goto done;
-
- /* driver constants */
- strcpy(tuner->name, "FM");
- tuner->type = V4L2_TUNER_RADIO;
- tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
- V4L2_TUNER_CAP_RDS;
-
- /* range limits */
- switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
- /* 0: 87.5 - 108 MHz (USA, Europe, default) */
- default:
- tuner->rangelow = 87.5 * FREQ_MUL;
- tuner->rangehigh = 108 * FREQ_MUL;
- break;
- /* 1: 76 - 108 MHz (Japan wide band) */
- case 1 :
- tuner->rangelow = 76 * FREQ_MUL;
- tuner->rangehigh = 108 * FREQ_MUL;
- break;
- /* 2: 76 - 90 MHz (Japan) */
- case 2 :
- tuner->rangelow = 76 * FREQ_MUL;
- tuner->rangehigh = 90 * FREQ_MUL;
- break;
- };
-
- /* stereo indicator == stereo (instead of mono) */
- if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
- else
- tuner->rxsubchans = V4L2_TUNER_SUB_MONO | 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. */
- tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
-
- /* mono/stereo selector */
- if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
- tuner->audmode = V4L2_TUNER_MODE_STEREO;
- else
- tuner->audmode = V4L2_TUNER_MODE_MONO;
-
- /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
- /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */
- tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
- /* the ideal factor is 0xffff/75 = 873,8 */
- tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
-
- /* 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)
- printk(KERN_WARNING DRIVER_NAME
- ": get tuner failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_tuner - set tuner attributes
- */
-static int si470x_vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *tuner)
-{
- struct si470x_device *radio = video_drvdata(file);
- int retval = -EINVAL;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (tuner->index != 0)
- goto done;
-
- /* mono/stereo selector */
- switch (tuner->audmode) {
- case V4L2_TUNER_MODE_MONO:
- radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
- break;
- case V4L2_TUNER_MODE_STEREO:
- radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
- break;
- default:
- goto done;
- }
-
- retval = si470x_set_register(radio, POWERCFG);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set tuner failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
- */
-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 */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (freq->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- freq->type = V4L2_TUNER_RADIO;
- retval = si470x_get_freq(radio, &freq->frequency);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": get frequency failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
- */
-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;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (freq->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- retval = si470x_set_freq(radio, freq->frequency);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set frequency failed with %d\n", retval);
- return retval;
-}
-
-
-/*
- * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
- */
-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;
-
- /* safety checks */
- if (radio->disconnected) {
- retval = -EIO;
- goto done;
- }
- if (seek->tuner != 0) {
- retval = -EINVAL;
- goto done;
- }
-
- retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
-
-done:
- if (retval < 0)
- printk(KERN_WARNING DRIVER_NAME
- ": set hardware frequency seek failed with %d\n",
- retval);
- return retval;
-}
-
-
-/*
- * 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,
-};
-
-
-/*
- * si470x_viddev_template - video device interface
- */
-static struct video_device si470x_viddev_template = {
- .fops = &si470x_fops,
- .name = DRIVER_NAME,
- .release = video_device_release,
- .ioctl_ops = &si470x_ioctl_ops,
-};
-
-
-
-/**************************************************************************
- * USB Interface
- **************************************************************************/
-
-/*
- * si470x_usb_driver_probe - probe for the device
- */
-static int si470x_usb_driver_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- struct si470x_device *radio;
- struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *endpoint;
- int i, int_end_size, retval = 0;
-
- /* private data allocation and initialization */
- radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
- if (!radio) {
- retval = -ENOMEM;
- goto err_initial;
- }
- radio->users = 0;
- radio->disconnected = 0;
- radio->usbdev = interface_to_usbdev(intf);
- radio->intf = intf;
- mutex_init(&radio->disconnect_lock);
- mutex_init(&radio->lock);
-
- iface_desc = intf->cur_altsetting;
-
- /* Set up interrupt endpoint information. */
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
- USB_DIR_IN) && ((endpoint->bmAttributes &
- USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
- radio->int_in_endpoint = endpoint;
- }
- if (!radio->int_in_endpoint) {
- printk(KERN_INFO DRIVER_NAME
- ": could not find interrupt in endpoint\n");
- retval = -EIO;
- goto err_radio;
- }
-
- int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize);
-
- radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL);
- if (!radio->int_in_buffer) {
- printk(KERN_INFO DRIVER_NAME
- "could not allocate int_in_buffer");
- retval = -ENOMEM;
- goto err_radio;
- }
-
- radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!radio->int_in_urb) {
- printk(KERN_INFO DRIVER_NAME "could not allocate int_in_urb");
- retval = -ENOMEM;
- goto err_intbuffer;
- }
-
- /* video device allocation and initialization */
- radio->videodev = video_device_alloc();
- if (!radio->videodev) {
- retval = -ENOMEM;
- goto err_intbuffer;
- }
- memcpy(radio->videodev, &si470x_viddev_template,
- sizeof(si470x_viddev_template));
- video_set_drvdata(radio->videodev, radio);
-
- /* show some infos about the specific si470x device */
- if (si470x_get_all_registers(radio) < 0) {
- retval = -EIO;
- goto err_video;
- }
- printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
- radio->registers[DEVICEID], radio->registers[CHIPID]);
-
- /* get software and hardware versions */
- if (si470x_get_scratch_page_versions(radio) < 0) {
- retval = -EIO;
- goto err_video;
- }
- printk(KERN_INFO DRIVER_NAME
- ": software version %d, hardware version %d\n",
- radio->software_version, radio->hardware_version);
-
- /* check if device and firmware is current */
- if ((radio->registers[CHIPID] & CHIPID_FIRMWARE)
- < RADIO_SW_VERSION_CURRENT) {
- printk(KERN_WARNING DRIVER_NAME
- ": This driver is known to work with "
- "firmware version %hu,\n", RADIO_SW_VERSION_CURRENT);
- printk(KERN_WARNING DRIVER_NAME
- ": but the device has firmware version %hu.\n",
- radio->registers[CHIPID] & CHIPID_FIRMWARE);
- printk(KERN_WARNING DRIVER_NAME
- ": If you have some trouble using this driver,\n");
- printk(KERN_WARNING DRIVER_NAME
- ": please report to V4L ML at "
- "linux-media@vger.kernel.org\n");
- }
-
- /* set initial frequency */
- si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
-
- /* set led to connect state */
- si470x_set_led_state(radio, BLINK_GREEN_LED);
-
- /* rds buffer allocation */
- radio->buf_size = rds_buf * 3;
- radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
- if (!radio->buffer) {
- retval = -EIO;
- goto err_video;
- }
-
- /* rds buffer configuration */
- radio->wr_index = 0;
- radio->rd_index = 0;
- init_waitqueue_head(&radio->read_queue);
-
- /* register video device */
- retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
- if (retval) {
- printk(KERN_WARNING DRIVER_NAME
- ": 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_intbuffer:
- kfree(radio->int_in_buffer);
-err_radio:
- kfree(radio);
-err_initial:
- return retval;
-}
-
-
-/*
- * si470x_usb_driver_suspend - suspend the device
- */
-static int si470x_usb_driver_suspend(struct usb_interface *intf,
- pm_message_t message)
-{
- printk(KERN_INFO DRIVER_NAME ": suspending now...\n");
-
- return 0;
-}
-
-
-/*
- * si470x_usb_driver_resume - resume the device
- */
-static int si470x_usb_driver_resume(struct usb_interface *intf)
-{
- printk(KERN_INFO DRIVER_NAME ": resuming now...\n");
-
- return 0;
-}
-
-
-/*
- * si470x_usb_driver_disconnect - disconnect the device
- */
-static void si470x_usb_driver_disconnect(struct usb_interface *intf)
-{
- struct si470x_device *radio = usb_get_intfdata(intf);
-
- mutex_lock(&radio->disconnect_lock);
- radio->disconnected = 1;
- 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);
- kfree(radio);
- }
- mutex_unlock(&radio->disconnect_lock);
-}
-
-
-/*
- * si470x_usb_driver - usb driver interface
- */
-static struct usb_driver si470x_usb_driver = {
- .name = DRIVER_NAME,
- .probe = si470x_usb_driver_probe,
- .disconnect = si470x_usb_driver_disconnect,
- .suspend = si470x_usb_driver_suspend,
- .resume = si470x_usb_driver_resume,
- .id_table = si470x_usb_driver_id_table,
- .supports_autosuspend = 1,
-};
-
-
-
-/**************************************************************************
- * Module Interface
- **************************************************************************/
-
-/*
- * si470x_module_init - module init
- */
-static int __init si470x_module_init(void)
-{
- printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
- return usb_register(&si470x_usb_driver);
-}
-
-
-/*
- * si470x_module_exit - module exit
- */
-static void __exit si470x_module_exit(void)
-{
- usb_deregister(&si470x_usb_driver);
-}
-
-
-module_init(si470x_module_init);
-module_exit(si470x_module_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_VERSION(DRIVER_VERSION);
diff --git a/linux/drivers/media/radio/si470x/Kconfig b/linux/drivers/media/radio/si470x/Kconfig
new file mode 100644
index 000000000..a466654ee
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/Kconfig
@@ -0,0 +1,37 @@
+config USB_SI470X
+ tristate "Silicon Labs Si470x FM Radio Receiver support with USB"
+ depends on USB && RADIO_SI470X
+ ---help---
+ This is a driver for USB devices with the Silicon Labs SI470x
+ chip. Currently these devices are known to work:
+ - 10c4:818a: Silicon Labs USB FM Radio Reference Design
+ - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music)
+ - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
+ - 10c5:819a: Sanei Electric FM USB Radio (aka DealExtreme.com PCear)
+
+ Sound is provided by the ALSA USB Audio/MIDI driver. Therefore
+ if you don't want to use the device solely for RDS receiving,
+ it is recommended to also select SND_USB_AUDIO.
+
+ Please have a look at the documentation, especially on how
+ to redirect the audio stream from the radio to your sound device:
+ Documentation/video4linux/si470x.txt
+
+ Say Y here if you want to connect this type of radio to your
+ computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-usb-si470x.
+
+config I2C_SI470X
+ tristate "Silicon Labs Si470x FM Radio Receiver support with I2C"
+ depends on I2C && RADIO_SI470X && !USB_SI470X
+ ---help---
+ This is a driver for I2C devices with the Silicon Labs SI470x
+ chip.
+
+ Say Y here if you want to connect this type of radio to your
+ computer's I2C port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-i2c-si470x.
diff --git a/linux/drivers/media/radio/si470x/Makefile b/linux/drivers/media/radio/si470x/Makefile
new file mode 100644
index 000000000..06964816c
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for radios with Silicon Labs Si470x FM Radio Receivers
+#
+
+radio-usb-si470x-objs := radio-si470x-usb.o radio-si470x-common.o
+radio-i2c-si470x-objs := radio-si470x-i2c.o radio-si470x-common.o
+
+obj-$(CONFIG_USB_SI470X) += radio-usb-si470x.o
+obj-$(CONFIG_I2C_SI470X) += radio-i2c-si470x.o
diff --git a/linux/drivers/media/radio/si470x/radio-si470x-common.c b/linux/drivers/media/radio/si470x/radio-si470x-common.c
new file mode 100644
index 000000000..f33315f2c
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x-common.c
@@ -0,0 +1,798 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x-common.c
+ *
+ * Driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * History:
+ * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.0
+ * - First working version
+ * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.1
+ * - Improved error handling, every function now returns errno
+ * - Improved multi user access (start/mute/stop)
+ * - Channel doesn't get lost anymore after start/mute/stop
+ * - RDS support added (polling mode via interrupt EP 1)
+ * - marked default module parameters with *value*
+ * - switched from bit structs to bit masks
+ * - header file cleaned and integrated
+ * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.2
+ * - hex values are now lower case
+ * - commented USB ID for ADS/Tech moved on todo list
+ * - blacklisted si470x in hid-quirks.c
+ * - rds buffer handling functions integrated into *_work, *_read
+ * - rds_command in si470x_poll exchanged against simple retval
+ * - check for firmware version 15
+ * - code order and prototypes still remain the same
+ * - spacing and bottom of band codes remain the same
+ * 2008-01-16 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.3
+ * - code reordered to avoid function prototypes
+ * - switch/case defaults are now more user-friendly
+ * - unified comment style
+ * - applied all checkpatch.pl v1.12 suggestions
+ * except the warning about the too long lines with bit comments
+ * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
+ * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.4
+ * - avoid poss. locking when doing copy_to_user which may sleep
+ * - RDS is automatically activated on read now
+ * - code cleaned of unnecessary rds_commands
+ * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
+ * (thanks to Guillaume RAMOUSSE)
+ * 2008-01-27 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.5
+ * - number of seek_retries changed to tune_timeout
+ * - fixed problem with incomplete tune operations by own buffers
+ * - optimization of variables and printf types
+ * - improved error logging
+ * 2008-01-31 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Oliver Neukum <oliver@neukum.org>
+ * Version 1.0.6
+ * - fixed coverity checker warnings in *_usb_driver_disconnect
+ * - probe()/open() race by correct ordering in probe()
+ * - DMA coherency rules by separate allocation of all buffers
+ * - use of endianness macros
+ * - abuse of spinlock, replaced by mutex
+ * - racy handling of timer in disconnect,
+ * replaced by delayed_work
+ * - racy interruptible_sleep_on(),
+ * replaced with wait_event_interruptible()
+ * - handle signals in read()
+ * 2008-02-08 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Oliver Neukum <oliver@neukum.org>
+ * Version 1.0.7
+ * - usb autosuspend support
+ * - unplugging fixed
+ * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.8
+ * - hardware frequency seek support
+ * - afc indication
+ * - more safety checks, let si470x_get_freq return errno
+ * - vidioc behavior corrected according to v4l2 spec
+ * 2008-10-20 Alexey Klimov <klimov.linux@gmail.com>
+ * - add support for KWorld USB FM Radio FM700
+ * - blacklisted KWorld radio in hid-core.c and hid-ids.h
+ * 2008-12-03 Mark Lord <mlord@pobox.com>
+ * - add support for DealExtreme USB Radio
+ * 2009-01-31 Bob Ross <pigiron@gmx.com>
+ * - correction of stereo detection/setting
+ * - correction of signal strength indicator scaling
+ * 2009-01-31 Rick Bronson <rick@efn.org>
+ * Tobias Lorenz <tobias.lorenz@gmx.net>
+ * - add LED status output
+ * - get HW/SW version from scratchpad
+ * 2009-06-16 Edouard Lafargue <edouard@lafargue.name>
+ * Version 1.0.10
+ * - add support for interrupt mode for RDS endpoint,
+ * instead of polling.
+ * Improves RDS reception significantly
+ */
+
+
+/* kernel includes */
+#include "radio-si470x.h"
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Spacing (kHz) */
+/* 0: 200 kHz (USA, Australia) */
+/* 1: 100 kHz (Europe, Japan) */
+/* 2: 50 kHz */
+static unsigned short space = 2;
+module_param(space, ushort, 0444);
+MODULE_PARM_DESC(space, "Spacing: 0=200kHz 1=100kHz *2=50kHz*");
+
+/* Bottom of Band (MHz) */
+/* 0: 87.5 - 108 MHz (USA, Europe)*/
+/* 1: 76 - 108 MHz (Japan wide band) */
+/* 2: 76 - 90 MHz (Japan) */
+static unsigned short band = 1;
+module_param(band, ushort, 0444);
+MODULE_PARM_DESC(band, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz");
+
+/* De-emphasis */
+/* 0: 75 us (USA) */
+/* 1: 50 us (Europe, Australia, Japan) */
+static unsigned short de = 1;
+module_param(de, ushort, 0444);
+MODULE_PARM_DESC(de, "De-emphasis: 0=75us *1=50us*");
+
+/* Tune timeout */
+static unsigned int tune_timeout = 3000;
+module_param(tune_timeout, uint, 0644);
+MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
+
+/* Seek timeout */
+static unsigned int seek_timeout = 5000;
+module_param(seek_timeout, uint, 0644);
+MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
+
+
+
+/**************************************************************************
+ * Generic Functions
+ **************************************************************************/
+
+/*
+ * si470x_set_chan - set the channel
+ */
+static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
+{
+ int retval;
+ unsigned long timeout;
+ bool timed_out = 0;
+
+ /* start tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_CHAN;
+ radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
+ retval = si470x_set_register(radio, CHANNEL);
+ if (retval < 0)
+ goto done;
+
+ /* wait till tune operation has completed */
+ timeout = jiffies + msecs_to_jiffies(tune_timeout);
+ do {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ goto stop;
+ timed_out = time_after(jiffies, timeout);
+ } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+ (!timed_out));
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+ dev_warn(&radio->videodev->dev, "tune does not complete\n");
+ if (timed_out)
+ dev_warn(&radio->videodev->dev,
+ "tune timed out after %u ms\n", tune_timeout);
+
+stop:
+ /* stop tuning */
+ radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
+ retval = si470x_set_register(radio, CHANNEL);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_get_freq - get the frequency
+ */
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
+{
+ unsigned int spacing, band_bottom;
+ unsigned short chan;
+ int retval;
+
+ /* Spacing (kHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0:
+ spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1:
+ spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default:
+ spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+ /* 0: 87.5 - 108 MHz (USA, Europe) */
+ case 0:
+ band_bottom = 87.5 * FREQ_MUL; break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ default:
+ band_bottom = 76 * FREQ_MUL; break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2:
+ band_bottom = 76 * FREQ_MUL; break;
+ };
+
+ /* read channel */
+ retval = si470x_get_register(radio, READCHAN);
+ chan = radio->registers[READCHAN] & READCHAN_READCHAN;
+
+ /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
+ *freq = chan * spacing + band_bottom;
+
+ return retval;
+}
+
+
+/*
+ * si470x_set_freq - set the frequency
+ */
+int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
+{
+ unsigned int spacing, band_bottom;
+ unsigned short chan;
+
+ /* Spacing (kHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_SPACE) >> 4) {
+ /* 0: 200 kHz (USA, Australia) */
+ case 0:
+ spacing = 0.200 * FREQ_MUL; break;
+ /* 1: 100 kHz (Europe, Japan) */
+ case 1:
+ spacing = 0.100 * FREQ_MUL; break;
+ /* 2: 50 kHz */
+ default:
+ spacing = 0.050 * FREQ_MUL; break;
+ };
+
+ /* Bottom of Band (MHz) */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+ /* 0: 87.5 - 108 MHz (USA, Europe) */
+ case 0:
+ band_bottom = 87.5 * FREQ_MUL; break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ default:
+ band_bottom = 76 * FREQ_MUL; break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2:
+ band_bottom = 76 * FREQ_MUL; break;
+ };
+
+ /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */
+ chan = (freq - band_bottom) / spacing;
+
+ return si470x_set_chan(radio, chan);
+}
+
+
+/*
+ * si470x_set_seek - set seek
+ */
+static int si470x_set_seek(struct si470x_device *radio,
+ unsigned int wrap_around, unsigned int seek_upward)
+{
+ int retval = 0;
+ unsigned long timeout;
+ bool timed_out = 0;
+
+ /* start seeking */
+ radio->registers[POWERCFG] |= POWERCFG_SEEK;
+ if (wrap_around == 1)
+ radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
+ else
+ radio->registers[POWERCFG] |= POWERCFG_SKMODE;
+ if (seek_upward == 1)
+ radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
+ else
+ radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
+ retval = si470x_set_register(radio, POWERCFG);
+ if (retval < 0)
+ goto done;
+
+ /* wait till seek operation has completed */
+ timeout = jiffies + msecs_to_jiffies(seek_timeout);
+ do {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ goto stop;
+ timed_out = time_after(jiffies, timeout);
+ } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+ (!timed_out));
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+ dev_warn(&radio->videodev->dev, "seek does not complete\n");
+ if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
+ 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;
+
+ return retval;
+}
+
+
+/*
+ * si470x_start - switch on radio
+ */
+int si470x_start(struct si470x_device *radio)
+{
+ int retval;
+
+ /* powercfg */
+ radio->registers[POWERCFG] =
+ POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
+ retval = si470x_set_register(radio, POWERCFG);
+ if (retval < 0)
+ goto done;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ goto done;
+
+ /* sysconfig 2 */
+ radio->registers[SYSCONFIG2] =
+ (0x3f << 8) | /* SEEKTH */
+ ((band << 6) & SYSCONFIG2_BAND) | /* BAND */
+ ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
+ 15; /* VOLUME (max) */
+ retval = si470x_set_register(radio, SYSCONFIG2);
+ if (retval < 0)
+ goto done;
+
+ /* reset last channel */
+ retval = si470x_set_chan(radio,
+ radio->registers[CHANNEL] & CHANNEL_CHAN);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_stop - switch off radio
+ */
+int si470x_stop(struct si470x_device *radio)
+{
+ int retval;
+
+ /* sysconfig 1 */
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ goto done;
+
+ /* powercfg */
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ /* POWERCFG_ENABLE has to automatically go low */
+ radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
+ retval = si470x_set_register(radio, POWERCFG);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_rds_on - switch on rds reception
+ */
+int si470x_rds_on(struct si470x_device *radio)
+{
+ int retval;
+
+ /* sysconfig 1 */
+ mutex_lock(&radio->lock);
+ radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
+ retval = si470x_set_register(radio, SYSCONFIG1);
+ if (retval < 0)
+ radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
+ mutex_unlock(&radio->lock);
+
+ return retval;
+}
+
+
+
+/**************************************************************************
+ * 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;
+
+ /* 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);
+ 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)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ 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;
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value == 1)
+ radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
+ else
+ radio->registers[POWERCFG] |= POWERCFG_DMUTE;
+ retval = si470x_set_register(radio, POWERCFG);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "set control failed with %d\n", retval);
+ 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;
+}
+
+
+/*
+ * si470x_vidioc_g_tuner - get tuner attributes
+ */
+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;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (tuner->index != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ goto done;
+
+ /* driver constants */
+ strcpy(tuner->name, "FM");
+ tuner->type = V4L2_TUNER_RADIO;
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+ tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_RDS;
+#else
+ tuner->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+#endif
+
+ /* range limits */
+ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) {
+ /* 0: 87.5 - 108 MHz (USA, Europe, default) */
+ default:
+ tuner->rangelow = 87.5 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 1: 76 - 108 MHz (Japan wide band) */
+ case 1:
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 108 * FREQ_MUL;
+ break;
+ /* 2: 76 - 90 MHz (Japan) */
+ case 2:
+ tuner->rangelow = 76 * FREQ_MUL;
+ tuner->rangehigh = 90 * FREQ_MUL;
+ break;
+ };
+
+ /* stereo indicator == stereo (instead of mono) */
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0)
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO;
+ else
+ tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+ /* If there is a reliable method of detecting an RDS channel,
+ then this code should check for that before setting this
+ RDS subchannel. */
+ tuner->rxsubchans |= V4L2_TUNER_SUB_RDS;
+#endif
+
+ /* mono/stereo selector */
+ if ((radio->registers[POWERCFG] & POWERCFG_MONO) == 0)
+ tuner->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ tuner->audmode = V4L2_TUNER_MODE_MONO;
+
+ /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */
+ /* measured in units of db쨉V in 1 db increments (max at ~75 db쨉V) */
+ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI);
+ /* the ideal factor is 0xffff/75 = 873,8 */
+ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10);
+
+ /* 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);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_tuner - set tuner attributes
+ */
+static int si470x_vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *tuner)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = -EINVAL;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (tuner->index != 0)
+ goto done;
+
+ /* mono/stereo selector */
+ switch (tuner->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ 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);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_g_frequency - get tuner or modulator radio frequency
+ */
+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 */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (freq->tuner != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ 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);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_frequency - set tuner or modulator radio frequency
+ */
+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;
+
+ /* safety checks */
+ retval = si470x_disconnect_check(radio);
+ if (retval)
+ goto done;
+
+ if (freq->tuner != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ retval = si470x_set_freq(radio, freq->frequency);
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "set frequency failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
+ */
+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;
+
+ /* 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);
+
+done:
+ if (retval < 0)
+ dev_warn(&radio->videodev->dev,
+ "set hardware frequency seek failed with %d\n", retval);
+ return retval;
+}
+
+
+/*
+ * 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,
+};
+
+
+/*
+ * si470x_viddev_template - video device interface
+ */
+struct video_device si470x_viddev_template = {
+ .fops = &si470x_fops,
+ .name = DRIVER_NAME,
+ .release = video_device_release,
+ .ioctl_ops = &si470x_ioctl_ops,
+};
diff --git a/linux/drivers/media/radio/si470x/radio-si470x-i2c.c b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c
new file mode 100644
index 000000000..2d53b6a94
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -0,0 +1,401 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x-i2c.c
+ *
+ * I2C driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * ToDo:
+ * - RDS support
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Joonyoung Shim <jy0922.shim@samsung.com>";
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 0)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "I2C radio driver for Si470x FM Radio Receivers"
+#define DRIVER_VERSION "1.0.0"
+
+/* kernel includes */
+#include <linux/i2c.h>
+#include <linux/delay.h>
+
+#include "radio-si470x.h"
+
+
+/* I2C Device ID List */
+static const struct i2c_device_id si470x_i2c_id[] = {
+ /* Generic Entry */
+ { "si470x", 0 },
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si470x_i2c_id);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+
+
+/**************************************************************************
+ * I2C Definitions
+ **************************************************************************/
+
+/* Write starts with the upper byte of register 0x02 */
+#define WRITE_REG_NUM 8
+#define WRITE_INDEX(i) (i + 0x02)
+
+/* Read starts with the upper byte of register 0x0a */
+#define READ_REG_NUM RADIO_REGISTER_NUM
+#define READ_INDEX(i) ((i + RADIO_REGISTER_NUM - 0x0a) % READ_REG_NUM)
+
+
+
+/**************************************************************************
+ * General Driver Functions - REGISTERs
+ **************************************************************************/
+
+/*
+ * si470x_get_register - read register
+ */
+int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+ u16 buf[READ_REG_NUM];
+ struct i2c_msg msgs[1] = {
+ { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
+ (void *)buf },
+ };
+
+ if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+ return -EIO;
+
+ radio->registers[regnr] = __be16_to_cpu(buf[READ_INDEX(regnr)]);
+
+ return 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+ int i;
+ u16 buf[WRITE_REG_NUM];
+ struct i2c_msg msgs[1] = {
+ { radio->client->addr, 0, sizeof(u16) * WRITE_REG_NUM,
+ (void *)buf },
+ };
+
+ for (i = 0; i < WRITE_REG_NUM; i++)
+ buf[i] = __cpu_to_be16(radio->registers[WRITE_INDEX(i)]);
+
+ if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - ENTIRE REGISTERS
+ **************************************************************************/
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+ int i;
+ u16 buf[READ_REG_NUM];
+ struct i2c_msg msgs[1] = {
+ { radio->client->addr, I2C_M_RD, sizeof(u16) * READ_REG_NUM,
+ (void *)buf },
+ };
+
+ if (i2c_transfer(radio->client->adapter, msgs, 1) != 1)
+ return -EIO;
+
+ for (i = 0; i < READ_REG_NUM; i++)
+ radio->registers[i] = __be16_to_cpu(buf[READ_INDEX(i)]);
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * 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
+ **************************************************************************/
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ mutex_lock(&radio->lock);
+ radio->users++;
+
+ if (radio->users == 1)
+ /* start radio */
+ retval = si470x_start(radio);
+
+ mutex_unlock(&radio->lock);
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static 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)
+ /* stop radio */
+ retval = si470x_stop(radio);
+
+ mutex_unlock(&radio->lock);
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+const struct v4l2_file_operations si470x_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = video_ioctl2,
+ .open = si470x_fops_open,
+ .release = si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ capability->version = DRIVER_KERNEL_VERSION;
+ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * I2C Interface
+ **************************************************************************/
+
+/*
+ * si470x_i2c_probe - probe for the device
+ */
+static int __devinit si470x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct si470x_device *radio;
+ int retval = 0;
+ unsigned char version_warning = 0;
+
+ /* private data allocation and initialization */
+ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
+ if (!radio) {
+ retval = -ENOMEM;
+ 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);
+
+ /* power up : need 110ms */
+ radio->registers[POWERCFG] = POWERCFG_ENABLE;
+ if (si470x_set_register(radio, POWERCFG) < 0) {
+ retval = -EIO;
+ goto err_all;
+ }
+ msleep(110);
+
+ /* get device and chip versions */
+ if (si470x_get_all_registers(radio) < 0) {
+ retval = -EIO;
+ goto err_video;
+ }
+ dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
+ radio->registers[DEVICEID], radio->registers[CHIPID]);
+ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) {
+ dev_warn(&client->dev,
+ "This driver is known to work with "
+ "firmware version %hu,\n", RADIO_FW_VERSION);
+ dev_warn(&client->dev,
+ "but the device has firmware version %hu.\n",
+ radio->registers[CHIPID] & CHIPID_FIRMWARE);
+ version_warning = 1;
+ }
+
+ /* give out version warning */
+ if (version_warning == 1) {
+ dev_warn(&client->dev,
+ "If you have some trouble using this driver,\n");
+ dev_warn(&client->dev,
+ "please report to V4L ML at "
+ "linux-media@vger.kernel.org\n");
+ }
+
+ /* set initial frequency */
+ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+ /* register video device */
+ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ radio_nr);
+ if (retval) {
+ dev_warn(&client->dev, "Could not register video device\n");
+ goto err_all;
+ }
+ i2c_set_clientdata(client, radio);
+
+ return 0;
+err_all:
+err_video:
+ video_device_release(radio->videodev);
+err_radio:
+ kfree(radio);
+err_initial:
+ return retval;
+}
+
+
+/*
+ * si470x_i2c_remove - remove the device
+ */
+static __devexit int si470x_i2c_remove(struct i2c_client *client)
+{
+ struct si470x_device *radio = i2c_get_clientdata(client);
+
+ video_unregister_device(radio->videodev);
+ kfree(radio);
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+
+/*
+ * si470x_i2c_driver - i2c driver interface
+ */
+static struct i2c_driver si470x_i2c_driver = {
+ .driver = {
+ .name = "si470x",
+ .owner = THIS_MODULE,
+ },
+ .probe = si470x_i2c_probe,
+ .remove = __devexit_p(si470x_i2c_remove),
+ .id_table = si470x_i2c_id,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_i2c_init - module init
+ */
+static int __init si470x_i2c_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+ return i2c_add_driver(&si470x_i2c_driver);
+}
+
+
+/*
+ * si470x_i2c_exit - module exit
+ */
+static void __exit si470x_i2c_exit(void)
+{
+ i2c_del_driver(&si470x_i2c_driver);
+}
+
+
+module_init(si470x_i2c_init);
+module_exit(si470x_i2c_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/linux/drivers/media/radio/si470x/radio-si470x-usb.c b/linux/drivers/media/radio/si470x/radio-si470x-usb.c
new file mode 100644
index 000000000..f2d0e1ddb
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -0,0 +1,988 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x-usb.c
+ *
+ * USB driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/*
+ * ToDo:
+ * - add firmware download/update support
+ */
+
+
+/* driver definitions */
+#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10)
+#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
+#define DRIVER_VERSION "1.0.10"
+
+/* kernel includes */
+#include <linux/usb.h>
+#include <linux/hid.h>
+
+#include "radio-si470x.h"
+
+
+/* USB Device ID List */
+static struct usb_device_id si470x_usb_driver_id_table[] = {
+ /* Silicon Labs USB FM Radio Reference Design */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
+ /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
+ /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },
+ /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },
+ /* Terminating entry */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table);
+
+
+
+/**************************************************************************
+ * Module Parameters
+ **************************************************************************/
+
+/* Radio Nr */
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio Nr");
+
+/* USB timeout */
+static unsigned int usb_timeout = 500;
+module_param(usb_timeout, uint, 0644);
+MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*");
+
+/* RDS buffer blocks */
+static unsigned int rds_buf = 100;
+module_param(rds_buf, uint, 0444);
+MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*");
+
+/* RDS maximum block errors */
+static unsigned short max_rds_errors = 1;
+/* 0 means 0 errors requiring correction */
+/* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */
+/* 2 means 3-5 errors requiring correction */
+/* 3 means 6+ errors or errors in checkword, correction not possible */
+module_param(max_rds_errors, ushort, 0644);
+MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
+
+
+
+/**************************************************************************
+ * USB HID Reports
+ **************************************************************************/
+
+/* Reports 1-16 give direct read/write access to the 16 Si470x registers */
+/* with the (REPORT_ID - 1) corresponding to the register address across USB */
+/* endpoint 0 using GET_REPORT and SET_REPORT */
+#define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1)
+#define REGISTER_REPORT(reg) ((reg) + 1)
+
+/* Report 17 gives direct read/write access to the entire Si470x register */
+/* map across endpoint 0 using GET_REPORT and SET_REPORT */
+#define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define ENTIRE_REPORT 17
+
+/* Report 18 is used to send the lowest 6 Si470x registers up the HID */
+/* interrupt endpoint 1 to Windows every 20 milliseconds for status */
+#define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1)
+#define RDS_REPORT 18
+
+/* Report 19: LED state */
+#define LED_REPORT_SIZE 3
+#define LED_REPORT 19
+
+/* Report 19: stream */
+#define STREAM_REPORT_SIZE 3
+#define STREAM_REPORT 19
+
+/* Report 20: scratch */
+#define SCRATCH_PAGE_SIZE 63
+#define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1)
+#define SCRATCH_REPORT 20
+
+/* Reports 19-22: flash upgrade of the C8051F321 */
+#define WRITE_REPORT_SIZE 4
+#define WRITE_REPORT 19
+#define FLASH_REPORT_SIZE 64
+#define FLASH_REPORT 20
+#define CRC_REPORT_SIZE 3
+#define CRC_REPORT 21
+#define RESPONSE_REPORT_SIZE 2
+#define RESPONSE_REPORT 22
+
+/* Report 23: currently unused, but can accept 60 byte reports on the HID */
+/* interrupt out endpoint 2 every 1 millisecond */
+#define UNUSED_REPORT 23
+
+
+
+/**************************************************************************
+ * Software/Hardware Versions from Scratch Page
+ **************************************************************************/
+#define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6
+#define RADIO_SW_VERSION 7
+#define RADIO_HW_VERSION 1
+
+
+
+/**************************************************************************
+ * LED State Definitions
+ **************************************************************************/
+#define LED_COMMAND 0x35
+
+#define NO_CHANGE_LED 0x00
+#define ALL_COLOR_LED 0x01 /* streaming state */
+#define BLINK_GREEN_LED 0x02 /* connect state */
+#define BLINK_RED_LED 0x04
+#define BLINK_ORANGE_LED 0x10 /* disconnect state */
+#define SOLID_GREEN_LED 0x20 /* tuning/seeking state */
+#define SOLID_RED_LED 0x40 /* bootload state */
+#define SOLID_ORANGE_LED 0x80
+
+
+
+/**************************************************************************
+ * Stream State Definitions
+ **************************************************************************/
+#define STREAM_COMMAND 0x36
+#define STREAM_VIDPID 0x00
+#define STREAM_AUDIO 0xff
+
+
+
+/**************************************************************************
+ * Bootloader / Flash Commands
+ **************************************************************************/
+
+/* unique id sent to bootloader and required to put into a bootload state */
+#define UNIQUE_BL_ID 0x34
+
+/* mask for the flash data */
+#define FLASH_DATA_MASK 0x55
+
+/* bootloader commands */
+#define GET_SW_VERSION_COMMAND 0x00
+#define SET_PAGE_COMMAND 0x01
+#define ERASE_PAGE_COMMAND 0x02
+#define WRITE_PAGE_COMMAND 0x03
+#define CRC_ON_PAGE_COMMAND 0x04
+#define READ_FLASH_BYTE_COMMAND 0x05
+#define RESET_DEVICE_COMMAND 0x06
+#define GET_HW_VERSION_COMMAND 0x07
+#define BLANK 0xff
+
+/* bootloader command responses */
+#define COMMAND_OK 0x01
+#define COMMAND_FAILED 0x02
+#define COMMAND_PENDING 0x03
+
+
+
+/**************************************************************************
+ * General Driver Functions - REGISTER_REPORTs
+ **************************************************************************/
+
+/*
+ * si470x_get_report - receive a HID report
+ */
+static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
+{
+ unsigned char *report = (unsigned char *) buf;
+ int retval;
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_rcvctrlpipe(radio->usbdev, 0),
+ HID_REQ_GET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
+ report[0], 2,
+ buf, size, usb_timeout);
+
+ if (retval < 0)
+ dev_warn(&radio->intf->dev,
+ "si470x_get_report: usb_control_msg returned %d\n",
+ retval);
+ return retval;
+}
+
+
+/*
+ * si470x_set_report - send a HID report
+ */
+static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
+{
+ unsigned char *report = (unsigned char *) buf;
+ int retval;
+
+ retval = usb_control_msg(radio->usbdev,
+ usb_sndctrlpipe(radio->usbdev, 0),
+ HID_REQ_SET_REPORT,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ report[0], 2,
+ buf, size, usb_timeout);
+
+ if (retval < 0)
+ dev_warn(&radio->intf->dev,
+ "si470x_set_report: usb_control_msg returned %d\n",
+ retval);
+ return retval;
+}
+
+
+/*
+ * si470x_get_register - read register
+ */
+int si470x_get_register(struct si470x_device *radio, int regnr)
+{
+ unsigned char buf[REGISTER_REPORT_SIZE];
+ int retval;
+
+ buf[0] = REGISTER_REPORT(regnr);
+
+ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+ if (retval >= 0)
+ radio->registers[regnr] = get_unaligned_be16(&buf[1]);
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+/*
+ * si470x_set_register - write register
+ */
+int si470x_set_register(struct si470x_device *radio, int regnr)
+{
+ unsigned char buf[REGISTER_REPORT_SIZE];
+ int retval;
+
+ buf[0] = REGISTER_REPORT(regnr);
+ put_unaligned_be16(radio->registers[regnr], &buf[1]);
+
+ retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - ENTIRE_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_get_all_registers - read entire registers
+ */
+static int si470x_get_all_registers(struct si470x_device *radio)
+{
+ unsigned char buf[ENTIRE_REPORT_SIZE];
+ int retval;
+ unsigned char regnr;
+
+ buf[0] = ENTIRE_REPORT;
+
+ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+ if (retval >= 0)
+ for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
+ radio->registers[regnr] = get_unaligned_be16(
+ &buf[regnr * RADIO_REGISTER_SIZE + 1]);
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - LED_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_set_led_state - sets the led state
+ */
+static int si470x_set_led_state(struct si470x_device *radio,
+ unsigned char led_state)
+{
+ unsigned char buf[LED_REPORT_SIZE];
+ int retval;
+
+ buf[0] = LED_REPORT;
+ buf[1] = LED_COMMAND;
+ buf[2] = led_state;
+
+ retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * General Driver Functions - SCRATCH_REPORT
+ **************************************************************************/
+
+/*
+ * si470x_get_scratch_versions - gets the scratch page and version infos
+ */
+static int si470x_get_scratch_page_versions(struct si470x_device *radio)
+{
+ unsigned char buf[SCRATCH_REPORT_SIZE];
+ int retval;
+
+ buf[0] = SCRATCH_REPORT;
+
+ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+
+ if (retval < 0)
+ dev_warn(&radio->intf->dev, "si470x_get_scratch: "
+ "si470x_get_report returned %d\n", retval);
+ else {
+ radio->software_version = buf[1];
+ radio->hardware_version = buf[2];
+ }
+
+ return (retval < 0) ? -EINVAL : 0;
+}
+
+
+
+/**************************************************************************
+ * 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
+ **************************************************************************/
+
+/*
+ * si470x_int_in_callback - rds callback and processing function
+ *
+ * TODO: do we need to use mutex locks in some sections?
+ */
+static void si470x_int_in_callback(struct urb *urb)
+{
+ struct si470x_device *radio = urb->context;
+ unsigned char buf[RDS_REPORT_SIZE];
+ int retval;
+ unsigned char regnr;
+ unsigned char blocknum;
+ unsigned short bler; /* rds block errors */
+ unsigned short rds;
+ unsigned char tmpbuf[3];
+
+ if (urb->status) {
+ if (urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN) {
+ return;
+ } else {
+ dev_warn(&radio->intf->dev,
+ "non-zero urb status (%d)\n", urb->status);
+ goto resubmit; /* Maybe we can recover. */
+ }
+ }
+
+ /* safety checks */
+ if (radio->disconnected)
+ return;
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ goto resubmit;
+
+ if (urb->actual_length > 0) {
+ /* Update RDS registers with URB data */
+ buf[0] = RDS_REPORT;
+ for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+ radio->registers[STATUSRSSI + regnr] =
+ get_unaligned_be16(&radio->int_in_buffer[
+ regnr * RADIO_REGISTER_SIZE + 1]);
+ /* get rds blocks */
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
+ /* No RDS group ready, better luck next time */
+ goto resubmit;
+ }
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) {
+ /* RDS decoder not synchronized */
+ goto resubmit;
+ }
+ for (blocknum = 0; blocknum < 4; blocknum++) {
+ switch (blocknum) {
+ default:
+ bler = (radio->registers[STATUSRSSI] &
+ STATUSRSSI_BLERA) >> 9;
+ rds = radio->registers[RDSA];
+ break;
+ case 1:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERB) >> 14;
+ rds = radio->registers[RDSB];
+ break;
+ case 2:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERC) >> 12;
+ rds = radio->registers[RDSC];
+ break;
+ case 3:
+ bler = (radio->registers[READCHAN] &
+ READCHAN_BLERD) >> 10;
+ rds = radio->registers[RDSD];
+ break;
+ };
+
+ /* Fill the V4L2 RDS buffer */
+ put_unaligned_le16(rds, &tmpbuf);
+ tmpbuf[2] = blocknum; /* offset name */
+ tmpbuf[2] |= blocknum << 3; /* received offset */
+ if (bler > max_rds_errors)
+ tmpbuf[2] |= 0x80; /* uncorrectable errors */
+ else if (bler > 0)
+ tmpbuf[2] |= 0x40; /* corrected error(s) */
+
+ /* copy RDS block to internal buffer */
+ memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3);
+ radio->wr_index += 3;
+
+ /* wrap write pointer */
+ if (radio->wr_index >= radio->buf_size)
+ radio->wr_index = 0;
+
+ /* check for overflow */
+ if (radio->wr_index == radio->rd_index) {
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+ }
+ }
+ if (radio->wr_index != radio->rd_index)
+ wake_up_interruptible(&radio->read_queue);
+ }
+
+resubmit:
+ /* Resubmit if we're still running. */
+ if (radio->int_in_running && radio->usbdev) {
+ retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC);
+ if (retval) {
+ dev_warn(&radio->intf->dev,
+ "resubmitting urb failed (%d)", retval);
+ radio->int_in_running = 0;
+ }
+ }
+}
+
+
+
+/**************************************************************************
+ * File Operations Interface
+ **************************************************************************/
+
+/*
+ * si470x_fops_read - read RDS data
+ */
+static ssize_t si470x_fops_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+ unsigned int block_count = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ si470x_rds_on(radio);
+
+ /* block if no new data available */
+ while (radio->wr_index == radio->rd_index) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EWOULDBLOCK;
+ goto done;
+ }
+ if (wait_event_interruptible(radio->read_queue,
+ radio->wr_index != radio->rd_index) < 0) {
+ retval = -EINTR;
+ goto done;
+ }
+ }
+
+ /* calculate block count from byte count */
+ count /= 3;
+
+ /* copy RDS block out of internal buffer and to user buffer */
+ mutex_lock(&radio->lock);
+ while (block_count < count) {
+ if (radio->rd_index == radio->wr_index)
+ break;
+
+ /* always transfer rds complete blocks */
+ if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3))
+ /* retval = -EFAULT; */
+ break;
+
+ /* increment and wrap read pointer */
+ radio->rd_index += 3;
+ if (radio->rd_index >= radio->buf_size)
+ radio->rd_index = 0;
+
+ /* increment counters */
+ block_count++;
+ buf += 3;
+ retval += 3;
+ }
+ mutex_unlock(&radio->lock);
+
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_fops_poll - poll RDS data
+ */
+static unsigned int si470x_fops_poll(struct file *file,
+ struct poll_table_struct *pts)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval = 0;
+
+ /* switch on rds reception */
+ if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
+ si470x_rds_on(radio);
+
+ poll_wait(file, &radio->read_queue, pts);
+
+ if (radio->rd_index != radio->wr_index)
+ retval = POLLIN | POLLRDNORM;
+
+ return retval;
+}
+
+
+/*
+ * si470x_fops_open - file open
+ */
+static int si470x_fops_open(struct file *file)
+{
+ struct si470x_device *radio = video_drvdata(file);
+ int retval;
+
+ lock_kernel();
+ 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:
+ unlock_kernel();
+ return retval;
+}
+
+
+/*
+ * si470x_fops_release - file release
+ */
+static 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->disconnect_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);
+ kfree(radio);
+ goto unlock;
+ }
+
+ /* cancel read processes */
+ wake_up_interruptible(&radio->read_queue);
+
+ /* stop radio */
+ retval = si470x_stop(radio);
+ usb_autopm_put_interface(radio->intf);
+ }
+unlock:
+ mutex_unlock(&radio->disconnect_lock);
+done:
+ return retval;
+}
+
+
+/*
+ * si470x_fops - file operations interface
+ */
+const struct v4l2_file_operations si470x_fops = {
+ .owner = THIS_MODULE,
+ .read = si470x_fops_read,
+ .poll = si470x_fops_poll,
+ .ioctl = video_ioctl2,
+ .open = si470x_fops_open,
+ .release = si470x_fops_release,
+};
+
+
+
+/**************************************************************************
+ * Video4Linux Interface
+ **************************************************************************/
+
+/*
+ * si470x_vidioc_querycap - query device capabilities
+ */
+int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+{
+ struct si470x_device *radio = video_drvdata(file);
+
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+ usb_make_path(radio->usbdev, capability->bus_info,
+ sizeof(capability->bus_info));
+ capability->version = DRIVER_KERNEL_VERSION;
+ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
+
+ return 0;
+}
+
+
+
+/**************************************************************************
+ * USB Interface
+ **************************************************************************/
+
+/*
+ * si470x_usb_driver_probe - probe for the device
+ */
+static int si470x_usb_driver_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct si470x_device *radio;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i, int_end_size, retval = 0;
+ unsigned char version_warning = 0;
+
+ /* private data allocation and initialization */
+ radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
+ if (!radio) {
+ retval = -ENOMEM;
+ goto err_initial;
+ }
+ radio->users = 0;
+ radio->disconnected = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
+ mutex_init(&radio->disconnect_lock);
+ mutex_init(&radio->lock);
+
+ iface_desc = intf->cur_altsetting;
+
+ /* Set up interrupt endpoint information. */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+ USB_DIR_IN) && ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
+ radio->int_in_endpoint = endpoint;
+ }
+ if (!radio->int_in_endpoint) {
+ dev_info(&intf->dev, "could not find interrupt in endpoint\n");
+ retval = -EIO;
+ goto err_radio;
+ }
+
+ int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize);
+
+ radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL);
+ if (!radio->int_in_buffer) {
+ dev_info(&intf->dev, "could not allocate int_in_buffer");
+ retval = -ENOMEM;
+ goto err_radio;
+ }
+
+ radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!radio->int_in_urb) {
+ dev_info(&intf->dev, "could not allocate int_in_urb");
+ retval = -ENOMEM;
+ goto err_intbuffer;
+ }
+
+ /* video device allocation and initialization */
+ radio->videodev = video_device_alloc();
+ if (!radio->videodev) {
+ retval = -ENOMEM;
+ goto err_intbuffer;
+ }
+ memcpy(radio->videodev, &si470x_viddev_template,
+ sizeof(si470x_viddev_template));
+ video_set_drvdata(radio->videodev, radio);
+
+ /* get device and chip versions */
+ if (si470x_get_all_registers(radio) < 0) {
+ retval = -EIO;
+ goto err_video;
+ }
+ dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
+ radio->registers[DEVICEID], radio->registers[CHIPID]);
+ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) {
+ dev_warn(&intf->dev,
+ "This driver is known to work with "
+ "firmware version %hu,\n", RADIO_FW_VERSION);
+ dev_warn(&intf->dev,
+ "but the device has firmware version %hu.\n",
+ radio->registers[CHIPID] & CHIPID_FIRMWARE);
+ version_warning = 1;
+ }
+
+ /* get software and hardware versions */
+ if (si470x_get_scratch_page_versions(radio) < 0) {
+ retval = -EIO;
+ goto err_video;
+ }
+ dev_info(&intf->dev, "software version %d, hardware version %d\n",
+ radio->software_version, radio->hardware_version);
+ if (radio->software_version < RADIO_SW_VERSION) {
+ dev_warn(&intf->dev,
+ "This driver is known to work with "
+ "software version %hu,\n", RADIO_SW_VERSION);
+ dev_warn(&intf->dev,
+ "but the device has software version %hu.\n",
+ radio->software_version);
+ version_warning = 1;
+ }
+ if (radio->hardware_version < RADIO_HW_VERSION) {
+ dev_warn(&intf->dev,
+ "This driver is known to work with "
+ "hardware version %hu,\n", RADIO_HW_VERSION);
+ dev_warn(&intf->dev,
+ "but the device has hardware version %hu.\n",
+ radio->hardware_version);
+ version_warning = 1;
+ }
+
+ /* give out version warning */
+ if (version_warning == 1) {
+ dev_warn(&intf->dev,
+ "If you have some trouble using this driver,\n");
+ dev_warn(&intf->dev,
+ "please report to V4L ML at "
+ "linux-media@vger.kernel.org\n");
+ }
+
+ /* set initial frequency */
+ si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */
+
+ /* set led to connect state */
+ si470x_set_led_state(radio, BLINK_GREEN_LED);
+
+ /* rds buffer allocation */
+ radio->buf_size = rds_buf * 3;
+ radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
+ if (!radio->buffer) {
+ retval = -EIO;
+ goto err_video;
+ }
+
+ /* rds buffer configuration */
+ radio->wr_index = 0;
+ radio->rd_index = 0;
+ init_waitqueue_head(&radio->read_queue);
+
+ /* register video device */
+ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO,
+ radio_nr);
+ if (retval) {
+ dev_warn(&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_intbuffer:
+ kfree(radio->int_in_buffer);
+err_radio:
+ kfree(radio);
+err_initial:
+ return retval;
+}
+
+
+/*
+ * si470x_usb_driver_suspend - suspend the device
+ */
+static int si470x_usb_driver_suspend(struct usb_interface *intf,
+ pm_message_t message)
+{
+ dev_info(&intf->dev, "suspending now...\n");
+
+ return 0;
+}
+
+
+/*
+ * si470x_usb_driver_resume - resume the device
+ */
+static int si470x_usb_driver_resume(struct usb_interface *intf)
+{
+ dev_info(&intf->dev, "resuming now...\n");
+
+ return 0;
+}
+
+
+/*
+ * si470x_usb_driver_disconnect - disconnect the device
+ */
+static void si470x_usb_driver_disconnect(struct usb_interface *intf)
+{
+ struct si470x_device *radio = usb_get_intfdata(intf);
+
+ mutex_lock(&radio->disconnect_lock);
+ radio->disconnected = 1;
+ 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);
+ kfree(radio);
+ }
+ mutex_unlock(&radio->disconnect_lock);
+}
+
+
+/*
+ * si470x_usb_driver - usb driver interface
+ */
+static struct usb_driver si470x_usb_driver = {
+ .name = DRIVER_NAME,
+ .probe = si470x_usb_driver_probe,
+ .disconnect = si470x_usb_driver_disconnect,
+ .suspend = si470x_usb_driver_suspend,
+ .resume = si470x_usb_driver_resume,
+ .id_table = si470x_usb_driver_id_table,
+ .supports_autosuspend = 1,
+};
+
+
+
+/**************************************************************************
+ * Module Interface
+ **************************************************************************/
+
+/*
+ * si470x_module_init - module init
+ */
+static int __init si470x_module_init(void)
+{
+ printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
+ return usb_register(&si470x_usb_driver);
+}
+
+
+/*
+ * si470x_module_exit - module exit
+ */
+static void __exit si470x_module_exit(void)
+{
+ usb_deregister(&si470x_usb_driver);
+}
+
+
+module_init(si470x_module_init);
+module_exit(si470x_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/linux/drivers/media/radio/si470x/radio-si470x.h b/linux/drivers/media/radio/si470x/radio-si470x.h
new file mode 100644
index 000000000..f931789c2
--- /dev/null
+++ b/linux/drivers/media/radio/si470x/radio-si470x.h
@@ -0,0 +1,226 @@
+/*
+ * drivers/media/radio/si470x/radio-si470x.h
+ *
+ * Driver for radios with Silicon Labs Si470x FM Radio Receivers
+ *
+ * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/* driver definitions */
+#define DRIVER_NAME "radio-si470x"
+
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/input.h>
+#include <linux/version.h>
+#include "compat.h"
+#include <linux/videodev2.h>
+#include <linux/mutex.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/rds.h>
+#include <asm/unaligned.h>
+
+
+
+/**************************************************************************
+ * Register Definitions
+ **************************************************************************/
+#define RADIO_REGISTER_SIZE 2 /* 16 register bit width */
+#define RADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */
+#define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */
+
+#define DEVICEID 0 /* Device ID */
+#define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */
+#define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */
+
+#define CHIPID 1 /* Chip ID */
+#define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */
+#define CHIPID_DEV 0x0200 /* bits 09..09: Device */
+#define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */
+
+#define POWERCFG 2 /* Power Configuration */
+#define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */
+#define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */
+#define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */
+#define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */
+#define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */
+#define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */
+#define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */
+#define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */
+#define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */
+
+#define CHANNEL 3 /* Channel */
+#define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */
+#define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */
+
+#define SYSCONFIG1 4 /* System Configuration 1 */
+#define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */
+#define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */
+#define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */
+#define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */
+#define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */
+#define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */
+#define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */
+#define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */
+#define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */
+
+#define SYSCONFIG2 5 /* System Configuration 2 */
+#define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */
+#define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */
+#define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */
+#define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */
+
+#define SYSCONFIG3 6 /* System Configuration 3 */
+#define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */
+#define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */
+#define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */
+#define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */
+
+#define TEST1 7 /* Test 1 */
+#define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */
+
+#define TEST2 8 /* Test 2 */
+/* TEST2 only contains reserved bits */
+
+#define BOOTCONFIG 9 /* Boot Configuration */
+/* BOOTCONFIG only contains reserved bits */
+
+#define STATUSRSSI 10 /* Status RSSI */
+#define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */
+#define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */
+#define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */
+#define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */
+#define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */
+#define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */
+#define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */
+#define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */
+
+#define READCHAN 11 /* Read Channel */
+#define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */
+#define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */
+#define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */
+#define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */
+
+#define RDSA 12 /* RDSA */
+#define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */
+
+#define RDSB 13 /* RDSB */
+#define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */
+
+#define RDSC 14 /* RDSC */
+#define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */
+
+#define RDSD 15 /* RDSD */
+#define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */
+
+
+
+/**************************************************************************
+ * General Driver Definitions
+ **************************************************************************/
+
+/*
+ * si470x_device - private data
+ */
+struct si470x_device {
+ struct video_device *videodev;
+
+ /* driver management */
+ unsigned int users;
+
+ /* Silabs internal registers (0..15) */
+ unsigned short registers[RADIO_REGISTER_NUM];
+
+ /* RDS receive buffer */
+ wait_queue_head_t read_queue;
+ struct mutex lock; /* buffer locking */
+ unsigned char *buffer; /* size is always multiple of three */
+ unsigned int buf_size;
+ unsigned int rd_index;
+ unsigned int wr_index;
+
+#if defined(CONFIG_USB_SI470X) || defined(CONFIG_USB_SI470X_MODULE)
+ /* reference to USB and video device */
+ struct usb_device *usbdev;
+ struct usb_interface *intf;
+
+ /* Interrupt endpoint handling */
+ char *int_in_buffer;
+ struct usb_endpoint_descriptor *int_in_endpoint;
+ struct urb *int_in_urb;
+ int int_in_running;
+
+ /* scratch page */
+ unsigned char software_version;
+ unsigned char hardware_version;
+
+ /* driver management */
+ unsigned char disconnected;
+ struct mutex disconnect_lock;
+#endif
+
+#if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE)
+ struct i2c_client *client;
+#endif
+};
+
+
+
+/**************************************************************************
+ * Firmware Versions
+ **************************************************************************/
+
+#define RADIO_FW_VERSION 15
+
+
+
+/**************************************************************************
+ * Frequency Multiplicator
+ **************************************************************************/
+
+/*
+ * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
+ * 62.5 kHz otherwise.
+ * The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
+ * tuner->capability is therefore set to V4L2_TUNER_CAP_LOW
+ * The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000
+ */
+#define FREQ_MUL (1000000 / 62.5)
+
+
+
+/**************************************************************************
+ * Common Functions
+ **************************************************************************/
+extern const struct v4l2_file_operations si470x_fops;
+extern struct video_device si470x_viddev_template;
+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);
+int si470x_set_freq(struct si470x_device *radio, unsigned int freq);
+int si470x_start(struct si470x_device *radio);
+int si470x_stop(struct si470x_device *radio);
+int si470x_rds_on(struct si470x_device *radio);
+int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability);
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig
index 8460013a0..14a1b6160 100644
--- a/linux/drivers/media/video/Kconfig
+++ b/linux/drivers/media/video/Kconfig
@@ -203,9 +203,9 @@ config VIDEO_CS53L32A
module will be called cs53l32a.
config VIDEO_M52790
- tristate "Mitsubishi M52790 A/V switch"
- depends on VIDEO_V4L2 && I2C
- ---help---
+ tristate "Mitsubishi M52790 A/V switch"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
Support for the Mitsubishi M52790 A/V switch.
To compile this driver as a module, choose M here: the
@@ -991,6 +991,8 @@ source "drivers/media/video/pwc/Kconfig"
config USB_ZR364XX
tristate "USB ZR364XX Camera support"
depends on VIDEO_V4L2
+ select VIDEOBUF_GEN
+ select VIDEOBUF_VMALLOC
---help---
Say Y here if you want to connect this type of camera to your
computer's USB port.
diff --git a/linux/drivers/media/video/bw-qcam.c b/linux/drivers/media/video/bw-qcam.c
index 3702baad6..8f6bb845e 100644
--- a/linux/drivers/media/video/bw-qcam.c
+++ b/linux/drivers/media/video/bw-qcam.c
@@ -993,7 +993,7 @@ static int accept_bwqcam(struct parport *port)
if (parport[0] && strncmp(parport[0], "auto", 4) != 0) {
/* user gave parport parameters */
- for(n=0; parport[n] && n<MAX_CAMS; n++){
+ for (n = 0; n < MAX_CAMS && parport[n]; n++) {
char *ep;
unsigned long r;
r = simple_strtoul(parport[n], &ep, 0);
diff --git a/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h
index a6f398a17..31a8759f6 100644
--- a/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h
+++ b/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h
@@ -60,10 +60,10 @@
#define PWR_RESETOUT_EN 0x100 /* bit8 */
enum AV_MODE{
- POLARIS_AVMODE_DEFAULT = 0,
- POLARIS_AVMODE_DIGITAL = 0x10,
- POLARIS_AVMODE_ANALOGT_TV = 0x20,
- POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
+ POLARIS_AVMODE_DEFAULT = 0,
+ POLARIS_AVMODE_DIGITAL = 0x10,
+ POLARIS_AVMODE_ANALOGT_TV = 0x20,
+ POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
};
diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c
index c3cfdd463..11b1982ca 100644
--- a/linux/drivers/media/video/cx88/cx88-cards.c
+++ b/linux/drivers/media/video/cx88/cx88-cards.c
@@ -1316,6 +1316,51 @@ static const struct cx88_board cx88_boards[] = {
},
.mpeg = CX88_MPEG_DVB,
},
+ [CX88_BOARD_WINFAST_DTV2000H_J] = {
+ .name = "WinFast DTV2000 H rev. J",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00017300,
+ .gpio1 = 0x00008207,
+ .gpio2 = 0x00000000,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x00018300,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x00018301,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00017304,
+ .gpio3 = 0x02000000,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x00015702,
+ .gpio1 = 0x0000f207,
+ .gpio2 = 0x00015702,
+ .gpio3 = 0x02000000,
+ },
+ .mpeg = CX88_MPEG_DVB,
+ },
[CX88_BOARD_GENIATECH_DVBS] = {
.name = "Geniatech DVB-S",
.tuner_type = TUNER_ABSENT,
@@ -1941,7 +1986,8 @@ static const struct cx88_board cx88_boards[] = {
.radio_addr = ADDR_UNSET,
.input = {{
.type = CX88_VMUX_DVB,
- .vmux = 1,
+ .vmux = 0,
+ .gpio0 = 0x8080,
} },
.mpeg = CX88_MPEG_DVB,
},
@@ -2315,6 +2361,10 @@ static const struct cx88_subid cx88_subids[] = {
.subdevice = 0x665e,
.card = CX88_BOARD_WINFAST_DTV2000H,
},{
+ .subvendor = 0x107d,
+ .subdevice = 0x6f2b,
+ .card = CX88_BOARD_WINFAST_DTV2000H_J,
+ },{
.subvendor = 0x18ac,
.subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */
.card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q,
@@ -3195,7 +3245,11 @@ static void cx88_card_setup(struct cx88_core *core)
case CX88_BOARD_PROF_6200:
case CX88_BOARD_PROF_7300:
case CX88_BOARD_SATTRADE_ST4200:
+ cx_write(MO_GP0_IO, 0x8000);
+ msleep(100);
cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_GP0_IO, 0x8080);
msleep(100);
cx_write(MO_SRST_IO, 1);
msleep(100);
diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c
index 9c88b6c0f..28042ad5a 100644
--- a/linux/drivers/media/video/cx88/cx88-dvb.c
+++ b/linux/drivers/media/video/cx88/cx88-dvb.c
@@ -425,17 +425,16 @@ static int tevii_dvbs_set_voltage(struct dvb_frontend *fe,
struct cx8802_dev *dev= fe->dvb->priv;
struct cx88_core *core = dev->core;
+ cx_set(MO_GP0_IO, 0x6040);
switch (voltage) {
case SEC_VOLTAGE_13:
- printk("LNB Voltage SEC_VOLTAGE_13\n");
- cx_write(MO_GP0_IO, 0x00006040);
+ cx_clear(MO_GP0_IO, 0x20);
break;
case SEC_VOLTAGE_18:
- printk("LNB Voltage SEC_VOLTAGE_18\n");
- cx_write(MO_GP0_IO, 0x00006060);
+ cx_set(MO_GP0_IO, 0x20);
break;
case SEC_VOLTAGE_OFF:
- printk("LNB Voltage SEC_VOLTAGE_off\n");
+ cx_clear(MO_GP0_IO, 0x20);
break;
}
@@ -500,14 +499,14 @@ static struct zl10353_config cx88_pinnacle_hybrid_pctv = {
};
static struct zl10353_config cx88_geniatech_x8000_mt = {
- .demod_address = (0x1e >> 1),
- .no_tuner = 1,
- .disable_i2c_gate_ctrl = 1,
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+ .disable_i2c_gate_ctrl = 1,
#if 0
- .input_frequency = 0xe609,
- .parallel_ts = 1,
- .r56_agc_targets = 0x2b,
- .r5c_clk_mpegts_output = 0x75,
+ .input_frequency = 0xe609,
+ .parallel_ts = 1,
+ .r56_agc_targets = 0x2b,
+ .r5c_clk_mpegts_output = 0x75,
#endif
};
@@ -703,6 +702,7 @@ static int dvb_register(struct cx8802_dev *dev)
}
break;
case CX88_BOARD_WINFAST_DTV2000H:
+ case CX88_BOARD_WINFAST_DTV2000H_J:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
case CX88_BOARD_HAUPPAUGE_HVR1300:
diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c
index 7c5e94916..6a3687a88 100644
--- a/linux/drivers/media/video/cx88/cx88-input.c
+++ b/linux/drivers/media/video/cx88/cx88-input.c
@@ -23,7 +23,7 @@
*/
#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/hrtimer.h>
#include <linux/input.h>
#include <linux/pci.h>
#include <linux/module.h>
@@ -49,7 +49,7 @@ struct cx88_IR {
/* poll external decoder */
int polling;
- struct delayed_work work;
+ struct hrtimer timer;
u32 gpio_addr;
u32 last_gpio;
u32 mask_keycode;
@@ -145,31 +145,28 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
}
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-static void cx88_ir_work(void *data)
-#else
-static void cx88_ir_work(struct work_struct *work)
-#endif
+static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer)
{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- struct cx88_IR *ir = data;
-#else
- struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work);
-#endif
+ unsigned long missed;
+ struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer);
cx88_ir_handle_key(ir);
- schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
+ missed = hrtimer_forward_now(&ir->timer,
+ ktime_set(0, ir->polling * 1000000));
+ if (missed > 1)
+ ir_dprintk("Missed ticks %ld\n", missed - 1);
+
+ return HRTIMER_RESTART;
}
void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir)
{
if (ir->polling) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- INIT_DELAYED_WORK(&ir->work, cx88_ir_work, ir);
-#else
- INIT_DELAYED_WORK(&ir->work, cx88_ir_work);
-#endif
- schedule_delayed_work(&ir->work, 0);
+ hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ir->timer.function = cx88_ir_work;
+ hrtimer_start(&ir->timer,
+ ktime_set(0, ir->polling * 1000000),
+ HRTIMER_MODE_REL);
}
if (ir->sampling) {
core->pci_irqmask |= PCI_INT_IR_SMPINT;
@@ -186,7 +183,7 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir)
}
if (ir->polling)
- cancel_delayed_work_sync(&ir->work);
+ hrtimer_cancel(&ir->timer);
}
/* ---------------------------------------------------------------------- */
@@ -238,6 +235,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir->sampling = 1;
break;
case CX88_BOARD_WINFAST_DTV2000H:
+ case CX88_BOARD_WINFAST_DTV2000H_J:
case CX88_BOARD_WINFAST_DTV1800H:
ir_codes = ir_codes_winfast;
ir->gpio_addr = MO_GP0_IO;
diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h
index 59fc01a53..014004acd 100644
--- a/linux/drivers/media/video/cx88/cx88.h
+++ b/linux/drivers/media/video/cx88/cx88.h
@@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
#define CX88_BOARD_HAUPPAUGE_IRONLY 80
#define CX88_BOARD_WINFAST_DTV1800H 81
+#define CX88_BOARD_WINFAST_DTV2000H_J 82
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c
index 02e251f8c..07644ec5e 100644
--- a/linux/drivers/media/video/em28xx/em28xx-cards.c
+++ b/linux/drivers/media/video/em28xx/em28xx-cards.c
@@ -231,7 +231,7 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = {
struct em28xx_board em28xx_boards[] = {
[EM2750_BOARD_UNKNOWN] = {
.name = "EM2710/EM2750/EM2751 webcam grabber",
- .xclk = EM28XX_XCLK_FREQUENCY_48MHZ,
+ .xclk = EM28XX_XCLK_FREQUENCY_20MHZ,
.tuner_type = TUNER_ABSENT,
.is_webcam = 1,
.no_audio = 1,
@@ -910,6 +910,8 @@ struct em28xx_board em28xx_boards[] = {
.decoder = EM28XX_TVP5150,
.has_dvb = 1,
.dvb_gpio = default_digital,
+ .ir_codes = ir_codes_terratec_cinergy_xs,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */
.input = { {
.type = EM28XX_VMUX_TELEVISION,
.vmux = TVP5150_COMPOSITE0,
@@ -1731,7 +1733,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
{0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF},
{0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
{0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028},
- {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
+ {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
{0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
{0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
};
@@ -1800,6 +1802,25 @@ static inline void em28xx_set_model(struct em28xx *dev)
EM28XX_I2C_FREQ_100_KHZ;
}
+
+/* FIXME: Should be replaced by a proper mt9m111 driver */
+static int em28xx_initialize_mt9m111(struct em28xx *dev)
+{
+ int i;
+ unsigned char regs[][3] = {
+ { 0x0d, 0x00, 0x01, }, /* reset and use defaults */
+ { 0x0d, 0x00, 0x00, },
+ { 0x0a, 0x00, 0x21, },
+ { 0x21, 0x04, 0x00, }, /* full readout speed, no row/col skipping */
+ };
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, &regs[i][0], 3);
+
+ return 0;
+}
+
+
/* FIXME: Should be replaced by a proper mt9m001 driver */
static int em28xx_initialize_mt9m001(struct em28xx *dev)
{
@@ -1828,7 +1849,7 @@ static int em28xx_initialize_mt9m001(struct em28xx *dev)
/* HINT method: webcam I2C chips
*
- * This method work for webcams with Micron sensors
+ * This method works for webcams with Micron sensors
*/
static int em28xx_hint_sensor(struct em28xx *dev)
{
@@ -1838,6 +1859,7 @@ static int em28xx_hint_sensor(struct em28xx *dev)
__be16 version_be;
u16 version;
+ /* Micron sensor detection */
dev->i2c_client.addr = 0xba >> 1;
cmd = 0;
i2c_master_send(&dev->i2c_client, &cmd, 1);
@@ -1846,28 +1868,58 @@ static int em28xx_hint_sensor(struct em28xx *dev)
return -EINVAL;
version = be16_to_cpu(version_be);
-
switch (version) {
case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */
case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */
dev->model = EM2820_BOARD_SILVERCREST_WEBCAM;
+ em28xx_set_model(dev);
+
sensor_name = "mt9v011";
dev->em28xx_sensor = EM28XX_MT9V011;
dev->sensor_xres = 640;
dev->sensor_yres = 480;
- dev->sensor_xtal = 12150000;
+ /*
+ * FIXME: mt9v011 uses I2S speed as xtal clk - at least with
+ * the Silvercrest cam I have here for testing - for higher
+ * resolutions, a high clock cause horizontal artifacts, so we
+ * need to use a lower xclk frequency.
+ * Yet, it would be possible to adjust xclk depending on the
+ * desired resolution, since this affects directly the
+ * frame rate.
+ */
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ;
+ dev->sensor_xtal = 4300000;
/* probably means GRGB 16 bit bayer */
dev->vinmode = 0x0d;
dev->vinctl = 0x00;
break;
+
+ case 0x143a: /* MT9M111 as found in the ECS G200 */
+ dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
+ sensor_name = "mt9m111";
+ dev->board.xclk = EM28XX_XCLK_FREQUENCY_48MHZ;
+ dev->em28xx_sensor = EM28XX_MT9M111;
+ em28xx_initialize_mt9m111(dev);
+ dev->sensor_xres = 640;
+ dev->sensor_yres = 512;
+
+ dev->vinmode = 0x0a;
+ dev->vinctl = 0x00;
+
+ break;
+
#if 0
case 0x8411:
case 0x8421:
#endif
case 0x8431:
dev->model = EM2750_BOARD_UNKNOWN;
+ em28xx_set_model(dev);
+
sensor_name = "mt9m001";
dev->em28xx_sensor = EM28XX_MT9M001;
em28xx_initialize_mt9m001(dev);
@@ -1880,10 +1932,13 @@ static int em28xx_hint_sensor(struct em28xx *dev)
break;
default:
- printk("Unknown Micron Sensor 0x%04x\n", be16_to_cpu(version));
+ printk("Unknown Micron Sensor 0x%04x\n", version);
return -EINVAL;
}
+ /* Setup webcam defaults */
+ em28xx_pre_card_setup(dev);
+
em28xx_errdev("Sensor is %s, using model %s entry.\n",
sensor_name, em28xx_boards[dev->model].name);
@@ -1895,63 +1950,6 @@ static int em28xx_hint_sensor(struct em28xx *dev)
*/
void em28xx_pre_card_setup(struct em28xx *dev)
{
- int rc;
-
- em28xx_set_model(dev);
-
- em28xx_info("Identified as %s (card=%d)\n",
- dev->board.name, dev->model);
-
- /* Set the default GPO/GPIO for legacy devices */
- dev->reg_gpo_num = EM2880_R04_GPO;
- dev->reg_gpio_num = EM28XX_R08_GPIO;
-
- dev->wait_after_write = 5;
-
- /* Based on the Chip ID, set the device configuration */
- rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
- if (rc > 0) {
- dev->chip_id = rc;
-
- switch (dev->chip_id) {
- case CHIP_ID_EM2710:
- em28xx_info("chip ID is em2710\n");
- break;
- case CHIP_ID_EM2750:
- em28xx_info("chip ID is em2750\n");
- break;
- case CHIP_ID_EM2820:
- em28xx_info("chip ID is em2820 (or em2710)\n");
- break;
- case CHIP_ID_EM2840:
- em28xx_info("chip ID is em2840\n");
- break;
- case CHIP_ID_EM2860:
- em28xx_info("chip ID is em2860\n");
- break;
- case CHIP_ID_EM2870:
- em28xx_info("chip ID is em2870\n");
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2874:
- em28xx_info("chip ID is em2874\n");
- dev->reg_gpio_num = EM2874_R80_GPIO;
- dev->wait_after_write = 0;
- break;
- case CHIP_ID_EM2883:
- em28xx_info("chip ID is em2882/em2883\n");
- dev->wait_after_write = 0;
- break;
- default:
- em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
- }
- }
-
- /* Prepopulate cached GPO register content */
- rc = em28xx_read_reg(dev, dev->reg_gpo_num);
- if (rc >= 0)
- dev->reg_gpo = rc;
-
/* Set the initial XCLK and I2C clock values based on the board
definition */
em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f);
@@ -2334,7 +2332,20 @@ void em28xx_register_i2c_ir(struct em28xx *dev)
void em28xx_card_setup(struct em28xx *dev)
{
- em28xx_set_model(dev);
+ /*
+ * If the device can be a webcam, seek for a sensor.
+ * If sensor is not found, then it isn't a webcam.
+ */
+ if (dev->board.is_webcam) {
+ if (em28xx_hint_sensor(dev) < 0)
+ dev->board.is_webcam = 0;
+ else
+ dev->progressive = 1;
+ } else
+ em28xx_set_model(dev);
+
+ em28xx_info("Identified as %s (card=%d)\n",
+ dev->board.name, dev->model);
dev->tuner_type = em28xx_boards[dev->model].tuner_type;
if (em28xx_boards[dev->model].tuner_addr)
@@ -2487,7 +2498,9 @@ void em28xx_card_setup(struct em28xx *dev)
}
em28xx_tuner_setup(dev);
- em28xx_ir_init(dev);
+
+ if(!disable_ir)
+ em28xx_ir_init(dev);
}
@@ -2564,7 +2577,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
int minor)
{
struct em28xx *dev = *devhandle;
- int retval = -ENOMEM;
+ int retval;
int errCode;
dev->udev = udev;
@@ -2581,6 +2594,58 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->em28xx_read_reg_req = em28xx_read_reg_req;
dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800;
+ em28xx_set_model(dev);
+
+ /* Set the default GPO/GPIO for legacy devices */
+ dev->reg_gpo_num = EM2880_R04_GPO;
+ dev->reg_gpio_num = EM28XX_R08_GPIO;
+
+ dev->wait_after_write = 5;
+
+ /* Based on the Chip ID, set the device configuration */
+ retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
+ if (retval > 0) {
+ dev->chip_id = retval;
+
+ switch (dev->chip_id) {
+ case CHIP_ID_EM2710:
+ em28xx_info("chip ID is em2710\n");
+ break;
+ case CHIP_ID_EM2750:
+ em28xx_info("chip ID is em2750\n");
+ break;
+ case CHIP_ID_EM2820:
+ em28xx_info("chip ID is em2820 (or em2710)\n");
+ break;
+ case CHIP_ID_EM2840:
+ em28xx_info("chip ID is em2840\n");
+ break;
+ case CHIP_ID_EM2860:
+ em28xx_info("chip ID is em2860\n");
+ break;
+ case CHIP_ID_EM2870:
+ em28xx_info("chip ID is em2870\n");
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2874:
+ em28xx_info("chip ID is em2874\n");
+ dev->reg_gpio_num = EM2874_R80_GPIO;
+ dev->wait_after_write = 0;
+ break;
+ case CHIP_ID_EM2883:
+ em28xx_info("chip ID is em2882/em2883\n");
+ dev->wait_after_write = 0;
+ break;
+ default:
+ em28xx_info("em28xx chip ID = %d\n", dev->chip_id);
+ }
+ }
+
+ /* Prepopulate cached GPO register content */
+ retval = em28xx_read_reg(dev, dev->reg_gpo_num);
+ if (retval >= 0)
+ dev->reg_gpo = retval;
+
em28xx_pre_card_setup(dev);
if (!dev->board.is_em2800) {
@@ -2615,18 +2680,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
dev->vinmode = 0x10;
dev->vinctl = 0x11;
- /*
- * If the device can be a webcam, seek for a sensor.
- * If sensor is not found, then it isn't a webcam.
- */
- if (dev->board.is_webcam)
- if (em28xx_hint_sensor(dev) < 0)
- dev->board.is_webcam = 0;
-
- /* It makes no sense to use de-interlacing mode on webcams */
- if (dev->board.is_webcam)
- dev->progressive = 1;
-
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c
index 10da3ad82..46cb13182 100644
--- a/linux/drivers/media/video/em28xx/em28xx-core.c
+++ b/linux/drivers/media/video/em28xx/em28xx-core.c
@@ -632,6 +632,9 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
+ if (dev->board.is_webcam)
+ rc = em28xx_write_reg(dev, 0x13, 0x0c);
+
/* enable video capture */
rc = em28xx_write_reg(dev, 0x48, 0x00);
diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c
index e32267383..3d522d547 100644
--- a/linux/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c
@@ -479,7 +479,6 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_KWORLD_DVB_310U:
- case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_with_xc3028,
&dev->i2c_adap);
@@ -489,6 +488,7 @@ static int dvb_init(struct em28xx *dev)
}
break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_EMPIRE_DUAL_TV:
dvb->frontend = dvb_attach(zl10353_attach,
&em28xx_zl10353_xc3028_no_i2c_gate,
&dev->i2c_adap);
diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c
index 77ec51dad..c7d723a03 100644
--- a/linux/drivers/media/video/em28xx/em28xx-video.c
+++ b/linux/drivers/media/video/em28xx/em28xx-video.c
@@ -1342,8 +1342,9 @@ static int vidioc_g_register(struct file *file, void *priv,
v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
return 0;
case V4L2_CHIP_MATCH_I2C_ADDR:
- /* Not supported yet */
- return -EINVAL;
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
+ return 0;
default:
if (!v4l2_chip_match_host(&reg->match))
return -EINVAL;
@@ -1394,8 +1395,9 @@ static int vidioc_s_register(struct file *file, void *priv,
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
return 0;
case V4L2_CHIP_MATCH_I2C_ADDR:
- /* Not supported yet */
- return -EINVAL;
+ /* TODO: is this correct? */
+ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
+ return 0;
default:
if (!v4l2_chip_match_host(&reg->match))
return -EINVAL;
diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h
index c76da6985..8203c6e1d 100644
--- a/linux/drivers/media/video/em28xx/em28xx.h
+++ b/linux/drivers/media/video/em28xx/em28xx.h
@@ -368,6 +368,7 @@ enum em28xx_sensor {
EM28XX_NOSENSOR = 0,
EM28XX_MT9V011,
EM28XX_MT9M001,
+ EM28XX_MT9M111,
};
enum em28xx_adecoder {
diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig
index 34f46f2bc..8897283b0 100644
--- a/linux/drivers/media/video/gspca/Kconfig
+++ b/linux/drivers/media/video/gspca/Kconfig
@@ -47,6 +47,15 @@ config USB_GSPCA_FINEPIX
To compile this driver as a module, choose M here: the
module will be called gspca_finepix.
+config USB_GSPCA_JEILINJ
+ tristate "Jeilin JPEG USB V4L2 driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+ Say Y here if you want support for cameras based on this Jeilin chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_jeilinj.
+
config USB_GSPCA_MARS
tristate "Mars USB Camera Driver"
depends on VIDEO_V4L2 && USB_GSPCA
@@ -103,9 +112,9 @@ config USB_GSPCA_PAC7311
module will be called gspca_pac7311.
config USB_GSPCA_SN9C20X
- tristate "SN9C20X USB Camera Driver"
- depends on VIDEO_V4L2 && USB_GSPCA
- help
+ tristate "SN9C20X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
Say Y here if you want support for cameras based on the
sn9c20x chips (SN9C201 and SN9C202).
@@ -113,10 +122,10 @@ config USB_GSPCA_SN9C20X
module will be called gspca_sn9c20x.
config USB_GSPCA_SN9C20X_EVDEV
- bool "Enable evdev support"
- depends on USB_GSPCA_SN9C20X
- ---help---
- Say Y here in order to enable evdev support for sn9c20x webcam button.
+ bool "Enable evdev support"
+ depends on USB_GSPCA_SN9C20X && INPUT
+ ---help---
+ Say Y here in order to enable evdev support for sn9c20x webcam button.
config USB_GSPCA_SONIXB
tristate "SONIX Bayer USB Camera Driver"
diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile
index f6d3b86e9..035616b5e 100644
--- a/linux/drivers/media/video/gspca/Makefile
+++ b/linux/drivers/media/video/gspca/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_USB_GSPCA) += gspca_main.o
obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
+obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o
obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
@@ -30,6 +31,7 @@ gspca_main-objs := gspca.o
gspca_conex-objs := conex.o
gspca_etoms-objs := etoms.o
gspca_finepix-objs := finepix.o
+gspca_jeilinj-objs := jeilinj.o
gspca_mars-objs := mars.o
gspca_mr97310a-objs := mr97310a.o
gspca_ov519-objs := ov519.o
diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c
index 8d48ea174..eca003566 100644
--- a/linux/drivers/media/video/gspca/conex.c
+++ b/linux/drivers/media/video/gspca/conex.c
@@ -820,7 +820,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
sd->brightness = BRIGHTNESS_DEF;
sd->contrast = CONTRAST_DEF;
diff --git a/linux/drivers/media/video/gspca/etoms.c b/linux/drivers/media/video/gspca/etoms.c
index 84f3a5eb8..42b36b8b7 100644
--- a/linux/drivers/media/video/gspca/etoms.c
+++ b/linux/drivers/media/video/gspca/etoms.c
@@ -635,10 +635,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor = id->driver_info;
if (sd->sensor == SENSOR_PAS106) {
cam->cam_mode = sif_mode;
- cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
+ cam->nmodes = ARRAY_SIZE(sif_mode);
} else {
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
gspca_dev->ctrl_dis = (1 << COLOR_IDX);
}
sd->brightness = BRIGHTNESS_DEF;
diff --git a/linux/drivers/media/video/gspca/jeilinj.c b/linux/drivers/media/video/gspca/jeilinj.c
new file mode 100644
index 000000000..dbfa3ed6e
--- /dev/null
+++ b/linux/drivers/media/video/gspca/jeilinj.c
@@ -0,0 +1,388 @@
+/*
+ * Jeilinj subdriver
+ *
+ * Supports some Jeilin dual-mode cameras which use bulk transport and
+ * download raw JPEG data.
+ *
+ * Copyright (C) 2009 Theodore Kilgore
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define MODULE_NAME "jeilinj"
+
+#include <linux/workqueue.h>
+#include "gspca.h"
+#include "jpeg.h"
+
+MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
+MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
+MODULE_LICENSE("GPL");
+
+/* Default timeouts, in ms */
+#define JEILINJ_CMD_TIMEOUT 500
+#define JEILINJ_DATA_TIMEOUT 1000
+
+/* Maximum transfer size to use. */
+#define JEILINJ_MAX_TRANSFER 0x200
+
+#define FRAME_HEADER_LEN 0x10
+
+/* Structure to hold all of our device specific stuff */
+struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+ const struct v4l2_pix_format *cap_mode;
+ /* Driver stuff */
+ struct work_struct work_struct;
+ struct workqueue_struct *work_thread;
+ u8 quality; /* image quality */
+ u8 jpegqual; /* webcam quality */
+ u8 *jpeg_hdr;
+};
+
+ struct jlj_command {
+ unsigned char instruction[2];
+ unsigned char ack_wanted;
+ };
+
+/* AFAICT these cameras will only do 320x240. */
+static struct v4l2_pix_format jlj_mode[] = {
+ { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0}
+};
+
+/*
+ * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
+ * and 0x82 for bulk transfer.
+ */
+
+/* All commands are two bytes only */
+static int jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command)
+{
+ int retval;
+
+ memcpy(gspca_dev->usb_buf, command, 2);
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_sndbulkpipe(gspca_dev->dev, 3),
+ gspca_dev->usb_buf, 2, NULL, 500);
+ if (retval < 0)
+ PDEBUG(D_ERR, "command write [%02x] error %d",
+ gspca_dev->usb_buf[0], retval);
+ return retval;
+}
+
+/* Responses are one byte only */
+static int jlj_read1(struct gspca_dev *gspca_dev, unsigned char response)
+{
+ int retval;
+
+ retval = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x84),
+ gspca_dev->usb_buf, 1, NULL, 500);
+ response = gspca_dev->usb_buf[0];
+ if (retval < 0)
+ PDEBUG(D_ERR, "read command [%02x] error %d",
+ gspca_dev->usb_buf[0], retval);
+ return retval;
+}
+
+static int jlj_start(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval = -1;
+ u8 response = 0xff;
+ struct jlj_command start_commands[] = {
+ {{0x71, 0x81}, 0},
+ {{0x70, 0x05}, 0},
+ {{0x95, 0x70}, 1},
+ {{0x71, 0x81}, 0},
+ {{0x70, 0x04}, 0},
+ {{0x95, 0x70}, 1},
+ {{0x71, 0x00}, 0},
+ {{0x70, 0x08}, 0},
+ {{0x95, 0x70}, 1},
+ {{0x94, 0x02}, 0},
+ {{0xde, 0x24}, 0},
+ {{0x94, 0x02}, 0},
+ {{0xdd, 0xf0}, 0},
+ {{0x94, 0x02}, 0},
+ {{0xe3, 0x2c}, 0},
+ {{0x94, 0x02}, 0},
+ {{0xe4, 0x00}, 0},
+ {{0x94, 0x02}, 0},
+ {{0xe5, 0x00}, 0},
+ {{0x94, 0x02}, 0},
+ {{0xe6, 0x2c}, 0},
+ {{0x94, 0x03}, 0},
+ {{0xaa, 0x00}, 0},
+ {{0x71, 0x1e}, 0},
+ {{0x70, 0x06}, 0},
+ {{0x71, 0x80}, 0},
+ {{0x70, 0x07}, 0}
+ };
+ for (i = 0; i < ARRAY_SIZE(start_commands); i++) {
+ retval = jlj_write2(gspca_dev, start_commands[i].instruction);
+ if (retval < 0)
+ return retval;
+ if (start_commands[i].ack_wanted)
+ retval = jlj_read1(gspca_dev, response);
+ if (retval < 0)
+ return retval;
+ }
+ PDEBUG(D_ERR, "jlj_start retval is %d", retval);
+ return retval;
+}
+
+static int jlj_stop(struct gspca_dev *gspca_dev)
+{
+ int i;
+ int retval;
+ struct jlj_command stop_commands[] = {
+ {{0x71, 0x00}, 0},
+ {{0x70, 0x09}, 0},
+ {{0x71, 0x80}, 0},
+ {{0x70, 0x05}, 0}
+ };
+ for (i = 0; i < ARRAY_SIZE(stop_commands); i++) {
+ retval = jlj_write2(gspca_dev, stop_commands[i].instruction);
+ if (retval < 0)
+ return retval;
+ }
+ return retval;
+}
+
+/* This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+ * threads attempting to use the camera's USB interface the gspca usb_lock is
+ * used when performing the one USB control operation inside the workqueue,
+ * which tells the camera to close the stream. In practice the only thing
+ * which needs to be protected against is the usb_set_interface call that
+ * gspca makes during stream_off. Otherwise the camera doesn't provide any
+ * controls that the user could try to change.
+ */
+
+static void jlj_dostream(struct work_struct *work)
+{
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ struct gspca_frame *frame;
+ int blocks_left; /* 0x200-sized blocks remaining in current frame. */
+ int size_in_blocks;
+ int act_len;
+ int discarding = 0; /* true if we failed to get space for frame. */
+ int packet_type;
+ int ret;
+ u8 *buffer;
+
+ buffer = kmalloc(JEILINJ_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ PDEBUG(D_ERR, "Couldn't allocate USB buffer");
+ goto quit_stream;
+ }
+ while (gspca_dev->present && gspca_dev->streaming) {
+ if (!gspca_dev->present)
+ goto quit_stream;
+ /* Start a new frame, and add the JPEG header, first thing */
+ frame = gspca_get_i_frame(gspca_dev);
+ if (frame && !discarding)
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ dev->jpeg_hdr, JPEG_HDR_SZ);
+ else
+ discarding = 1;
+ /*
+ * Now request data block 0. Line 0 reports the size
+ * to download, in blocks of size 0x200, and also tells the
+ * "actual" data size, in bytes, which seems best to ignore.
+ */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+ buffer, JEILINJ_MAX_TRANSFER, &act_len,
+ JEILINJ_DATA_TIMEOUT);
+ PDEBUG(D_STREAM,
+ "Got %d bytes out of %d for Block 0",
+ act_len, JEILINJ_MAX_TRANSFER);
+ if (ret < 0 || act_len < FRAME_HEADER_LEN)
+ goto quit_stream;
+ size_in_blocks = buffer[0x0a];
+ blocks_left = buffer[0x0a] - 1;
+ PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
+ packet_type = INTER_PACKET;
+ if (frame && !discarding)
+ /* Toss line 0 of data block 0, keep the rest. */
+ gspca_frame_add(gspca_dev, packet_type,
+ frame, buffer + FRAME_HEADER_LEN,
+ JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
+ else
+ discarding = 1;
+ while (blocks_left > 0) {
+ if (!gspca_dev->present)
+ goto quit_stream;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x82),
+ buffer, JEILINJ_MAX_TRANSFER, &act_len,
+ JEILINJ_DATA_TIMEOUT);
+ if (ret < 0 || act_len < JEILINJ_MAX_TRANSFER)
+ goto quit_stream;
+ PDEBUG(D_STREAM,
+ "%d blocks remaining for frame", blocks_left);
+ blocks_left -= 1;
+ if (blocks_left == 0)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+ if (frame && !discarding)
+ gspca_frame_add(gspca_dev, packet_type,
+ frame, buffer,
+ JEILINJ_MAX_TRANSFER);
+ else
+ discarding = 1;
+ }
+ }
+quit_stream:
+ mutex_lock(&gspca_dev->usb_lock);
+ if (gspca_dev->present)
+ jlj_stop(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ kfree(buffer);
+}
+
+/* This function is called at probe time just before sd_init */
+static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+{
+ struct cam *cam = &gspca_dev->cam;
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ dev->quality = 85;
+ dev->jpegqual = 85;
+ PDEBUG(D_PROBE,
+ "JEILINJ camera detected"
+ " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+ cam->cam_mode = jlj_mode;
+ cam->nmodes = 1;
+ cam->bulk = 1;
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk_size = 32;
+ INIT_WORK(&dev->work_struct, jlj_dostream);
+ return 0;
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+
+ /* wait for the work queue to terminate */
+ mutex_unlock(&gspca_dev->usb_lock);
+ /* This waits for jlj_dostream to finish */
+ destroy_workqueue(dev->work_thread);
+ dev->work_thread = NULL;
+ mutex_lock(&gspca_dev->usb_lock);
+ kfree(dev->jpeg_hdr);
+}
+
+/* this function is called at probe and resume time */
+static int sd_init(struct gspca_dev *gspca_dev)
+{
+ return 0;
+}
+
+/* Set up for getting frames. */
+static int sd_start(struct gspca_dev *gspca_dev)
+{
+ struct sd *dev = (struct sd *) gspca_dev;
+ int ret;
+
+ /* create the JPEG header */
+ dev->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+ jpeg_define(dev->jpeg_hdr, gspca_dev->height, gspca_dev->width,
+ 0x21); /* JPEG 422 */
+ jpeg_set_qual(dev->jpeg_hdr, dev->quality);
+ PDEBUG(D_STREAM, "Start streaming at 320x240");
+ ret = jlj_start(gspca_dev);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "Start streaming command failed");
+ return ret;
+ }
+ /* Start the workqueue function to do the streaming */
+ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
+ queue_work(dev->work_thread, &dev->work_struct);
+
+ return 0;
+}
+
+/* Table of supported USB devices */
+static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0979, 0x0280)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+/* sub-driver description */
+static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .config = sd_config,
+ .init = sd_init,
+ .start = sd_start,
+ .stop0 = sd_stop0,
+};
+
+/* -- device connect -- */
+static int sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return gspca_dev_probe(intf, id,
+ &sd_desc,
+ sizeof(struct sd),
+ THIS_MODULE);
+}
+
+static struct usb_driver sd_driver = {
+ .name = MODULE_NAME,
+ .id_table = device_table,
+ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+#ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+ .resume = gspca_resume,
+#endif
+};
+
+/* -- module insert / remove -- */
+static int __init sd_mod_init(void)
+{
+ int ret;
+
+ ret = usb_register(&sd_driver);
+ if (ret < 0)
+ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+}
+
+static void __exit sd_mod_exit(void)
+{
+ usb_deregister(&sd_driver);
+ PDEBUG(D_PROBE, "deregistered");
+}
+
+module_init(sd_mod_init);
+module_exit(sd_mod_exit);
diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
index 7127321ac..6b89f33a4 100644
--- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c
@@ -178,8 +178,10 @@ sensor_found:
sens_priv->settings =
kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL);
- if (!sens_priv->settings)
+ if (!sens_priv->settings) {
+ kfree(sens_priv);
return -ENOMEM;
+ }
sd->gspca_dev.cam.cam_mode = s5k83a_modes;
sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes);
diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c
index 98fb39c99..6a668deb1 100644
--- a/linux/drivers/media/video/gspca/sonixj.c
+++ b/linux/drivers/media/video/gspca/sonixj.c
@@ -741,7 +741,7 @@ static const u8 ov7660_sensor_init[][8] = {
/* COM 1 BAVE GEAVE AECHH */
{0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
{0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
-#if 1
+#if 0
{0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10},
#else
{0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10},
@@ -795,7 +795,7 @@ static const u8 ov7660_sensor_init[][8] = {
{0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
{0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
{0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
- {0xb1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
/****** (some exchanges in the win trace) ******/
{0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
/* bits[3..0]reserved */
@@ -1165,11 +1165,13 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
reg_w1(gspca_dev, 0x01, 0x42);
break;
case SENSOR_OV7660:
+#if 0
reg_w1(gspca_dev, 0x01, 0x61);
reg_w1(gspca_dev, 0x17, 0x20);
reg_w1(gspca_dev, 0x01, 0x60);
reg_w1(gspca_dev, 0x01, 0x40);
break;
+#endif
case SENSOR_SP80708:
reg_w1(gspca_dev, 0x01, 0x63);
reg_w1(gspca_dev, 0x17, 0x20);
@@ -1654,6 +1656,8 @@ static void setvflip(struct sd *sd)
static void setinfrared(struct sd *sd)
{
+ if (sd->gspca_dev.ctrl_dis & (1 << INFRARED_IDX))
+ return;
/*fixme: different sequence for StarCam Clip and StarCam 370i */
#if 1
/* Clip */
@@ -1675,14 +1679,14 @@ static void setfreq(struct gspca_dev *gspca_dev)
if (sd->sensor == SENSOR_OV7660) {
u8 com8;
-#if 1
- com8 = 0xf8; /* no auto gain/wb/expo */
+#if 0
+ com8 = 0xd8; /* no auto gain/wb/expo */
#else
- com8 = 0xff;
+ com8 = 0xdf; /* auto gain/wb/expo */
#endif
switch (sd->freq) {
case 0: /* Banding filter disabled */
- i2c_w1(gspca_dev, 0x13, com8 & 0xdf);
+ i2c_w1(gspca_dev, 0x13, com8 | 0x20);
break;
case 1: /* 50 hz */
i2c_w1(gspca_dev, 0x13, com8);
@@ -1839,12 +1843,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w1(gspca_dev, 0x99, 0x60);
break;
case SENSOR_OV7660:
+#if 0
reg_w1(gspca_dev, 0x9a, 0x05);
if (sd->bridge == BRIDGE_SN9C105)
reg_w1(gspca_dev, 0x99, 0xff);
else
reg_w1(gspca_dev, 0x99, 0x5b);
break;
+#endif
case SENSOR_SP80708:
reg_w1(gspca_dev, 0x9a, 0x05);
reg_w1(gspca_dev, 0x99, 0x59);
@@ -2373,7 +2379,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
{USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)},
/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */
{USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)},
-/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6801, 0x??)}, */
+/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */
/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */
{USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)},
/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */
diff --git a/linux/drivers/media/video/gspca/spca501.c b/linux/drivers/media/video/gspca/spca501.c
index 61dfc714f..9c4a2e6db 100644
--- a/linux/drivers/media/video/gspca/spca501.c
+++ b/linux/drivers/media/video/gspca/spca501.c
@@ -1948,7 +1948,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
sd->subtype = id->driver_info;
sd->brightness = sd_ctrls[MY_BRIGHTNESS].qctrl.default_value;
sd->contrast = sd_ctrls[MY_CONTRAST].qctrl.default_value;
diff --git a/linux/drivers/media/video/gspca/spca506.c b/linux/drivers/media/video/gspca/spca506.c
index 3a0c893f9..a199298a6 100644
--- a/linux/drivers/media/video/gspca/spca506.c
+++ b/linux/drivers/media/video/gspca/spca506.c
@@ -286,7 +286,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode);
sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c
index 0da8e0de0..7af511b5e 100644
--- a/linux/drivers/media/video/gspca/stv06xx/stv06xx.c
+++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c
@@ -130,8 +130,8 @@ int stv06xx_write_sensor_bytes(struct sd *sd, const u8 *data, u8 len)
STV06XX_URB_MSG_TIMEOUT);
if (err < 0)
return err;
- }
- return stv06xx_write_sensor_finish(sd);
+ }
+ return stv06xx_write_sensor_finish(sd);
}
int stv06xx_write_sensor_words(struct sd *sd, const u16 *data, u8 len)
diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c
index 270b86f24..33bac051d 100644
--- a/linux/drivers/media/video/gspca/sunplus.c
+++ b/linux/drivers/media/video/gspca/sunplus.c
@@ -872,7 +872,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* case BRIDGE_SPCA504: */
/* case BRIDGE_SPCA536: */
cam->cam_mode = vga_mode;
- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ cam->nmodes =ARRAY_SIZE(vga_mode);
break;
case BRIDGE_SPCA533:
cam->cam_mode = custom_mode;
@@ -883,7 +883,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
break;
case BRIDGE_SPCA504C:
cam->cam_mode = vga_mode2;
- cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0];
+ cam->nmodes = ARRAY_SIZE(vga_mode2);
break;
}
sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c
index 2a9480138..02b6e185f 100644
--- a/linux/drivers/media/video/gspca/zc3xx.c
+++ b/linux/drivers/media/video/gspca/zc3xx.c
@@ -7607,7 +7607,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
static const struct sd_desc sd_desc = {
.name = MODULE_NAME,
.ctrls = sd_ctrls,
- .nctrls = sizeof sd_ctrls / sizeof sd_ctrls[0],
+ .nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
.init = sd_init,
.start = sd_start,
diff --git a/linux/drivers/media/video/hdpvr/hdpvr-control.c b/linux/drivers/media/video/hdpvr/hdpvr-control.c
index 06791749d..5a6b78b8d 100644
--- a/linux/drivers/media/video/hdpvr/hdpvr-control.c
+++ b/linux/drivers/media/video/hdpvr/hdpvr-control.c
@@ -178,24 +178,24 @@ error:
int hdpvr_set_options(struct hdpvr_device *dev)
{
- hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std);
+ hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std);
- hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE,
+ hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE,
dev->options.video_input+1);
- hdpvr_set_audio(dev, dev->options.audio_input+1,
+ hdpvr_set_audio(dev, dev->options.audio_input+1,
dev->options.audio_codec);
- hdpvr_set_bitrate(dev);
- hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+ hdpvr_set_bitrate(dev);
+ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
dev->options.bitrate_mode);
- hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode);
+ hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode);
- hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness);
- hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast);
- hdpvr_config_call(dev, CTRL_HUE, dev->options.hue);
- hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation);
- hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness);
+ hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness);
+ hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast);
+ hdpvr_config_call(dev, CTRL_HUE, dev->options.hue);
+ hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation);
+ hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness);
- return 0;
+ return 0;
}
diff --git a/linux/drivers/media/video/hdpvr/hdpvr-video.c b/linux/drivers/media/video/hdpvr/hdpvr-video.c
index 5937de23c..2eb9dc2eb 100644
--- a/linux/drivers/media/video/hdpvr/hdpvr-video.c
+++ b/linux/drivers/media/video/hdpvr/hdpvr-video.c
@@ -1222,6 +1222,8 @@ static const struct video_device hdpvr_video_template = {
V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
V4L2_STD_PAL_60,
+ .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M |
+ V4L2_STD_PAL_60,
};
int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c
index e609d68c4..93052047c 100644
--- a/linux/drivers/media/video/mt9m001.c
+++ b/linux/drivers/media/video/mt9m001.c
@@ -13,13 +13,13 @@
#include <linux/i2c.h>
#include <linux/log2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
/* mt9m001 i2c address 0x5d
- * The platform has to define i2c_board_info
- * and call i2c_register_board_info() */
+ * The platform has to define ctruct i2c_board_info objects and link to them
+ * from struct soc_camera_link */
/* mt9m001 selected register addresses */
#define MT9M001_CHIP_VERSION 0x00
@@ -39,6 +39,13 @@
#define MT9M001_GLOBAL_GAIN 0x35
#define MT9M001_CHIP_ENABLE 0xF1
+#define MT9M001_MAX_WIDTH 1280
+#define MT9M001_MAX_HEIGHT 1024
+#define MT9M001_MIN_WIDTH 48
+#define MT9M001_MIN_HEIGHT 32
+#define MT9M001_COLUMN_SKIP 20
+#define MT9M001_ROW_SKIP 12
+
static const struct soc_camera_data_format mt9m001_colour_formats[] = {
/* Order important: first natively supported,
* second supported with a GPIO extender */
@@ -69,12 +76,20 @@ static const struct soc_camera_data_format mt9m001_monochrome_formats[] = {
};
struct mt9m001 {
- struct i2c_client *client;
- struct soc_camera_device icd;
+ struct v4l2_subdev subdev;
+ struct v4l2_rect rect; /* Sensor window */
+ __u32 fourcc;
int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
+ unsigned int gain;
+ unsigned int exposure;
unsigned char autoexposure;
};
+static struct mt9m001 *to_mt9m001(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9m001, subdev);
+}
+
static int reg_read(struct i2c_client *client, const u8 reg)
{
s32 data = i2c_smbus_read_word_data(client, reg);
@@ -109,35 +124,20 @@ static int reg_clear(struct i2c_client *client, const u8 reg,
return reg_write(client, reg, ret & ~data);
}
-static int mt9m001_init(struct soc_camera_device *icd)
+static int mt9m001_init(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct soc_camera_link *icl = client->dev.platform_data;
int ret;
- dev_dbg(icd->vdev->parent, "%s\n", __func__);
+ dev_dbg(&client->dev, "%s\n", __func__);
- if (icl->power) {
- ret = icl->power(&client->dev, 1);
- if (ret < 0) {
- dev_err(icd->vdev->parent,
- "Platform failed to power-on the camera.\n");
- return ret;
- }
- }
-
- /* The camera could have been already on, we reset it additionally */
- if (icl->reset)
- ret = icl->reset(&client->dev);
- else
- ret = -ENODEV;
+ /*
+ * We don't know, whether platform provides reset, issue a soft reset
+ * too. This returns all registers to their default values.
+ */
+ ret = reg_write(client, MT9M001_RESET, 1);
+ if (!ret)
+ ret = reg_write(client, MT9M001_RESET, 0);
- if (ret < 0) {
- /* Either no platform reset, or platform reset failed */
- ret = reg_write(client, MT9M001_RESET, 1);
- if (!ret)
- ret = reg_write(client, MT9M001_RESET, 0);
- }
/* Disable chip, synchronous option update */
if (!ret)
ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
@@ -145,36 +145,12 @@ static int mt9m001_init(struct soc_camera_device *icd)
return ret;
}
-static int mt9m001_release(struct soc_camera_device *icd)
+static int mt9m001_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct soc_camera_link *icl = client->dev.platform_data;
-
- /* Disable the chip */
- reg_write(client, MT9M001_OUTPUT_CONTROL, 0);
-
- if (icl->power)
- icl->power(&client->dev, 0);
-
- return 0;
-}
+ struct i2c_client *client = sd->priv;
-static int mt9m001_start_capture(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(icd->control);
-
- /* Switch to master "normal" mode */
- if (reg_write(client, MT9M001_OUTPUT_CONTROL, 2) < 0)
- return -EIO;
- return 0;
-}
-
-static int mt9m001_stop_capture(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(icd->control);
-
- /* Stop sensor readout */
- if (reg_write(client, MT9M001_OUTPUT_CONTROL, 0) < 0)
+ /* Switch to master "normal" mode or stop sensor readout */
+ if (reg_write(client, MT9M001_OUTPUT_CONTROL, enable ? 2 : 0) < 0)
return -EIO;
return 0;
}
@@ -182,8 +158,7 @@ static int mt9m001_stop_capture(struct soc_camera_device *icd)
static int mt9m001_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
- struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
/* Only one width bit may be set */
@@ -205,8 +180,7 @@ static int mt9m001_set_bus_param(struct soc_camera_device *icd,
static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
{
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
- struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
/* MT9M001 has all capture_format parameters fixed */
unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING |
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
@@ -220,13 +194,35 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
return soc_camera_apply_sensor_flags(icl, flags);
}
-static int mt9m001_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_rect rect = a->c;
+ struct soc_camera_device *icd = client->dev.platform_data;
int ret;
const u16 hblank = 9, vblank = 25;
+ unsigned int total_h;
+
+ if (mt9m001->fourcc == V4L2_PIX_FMT_SBGGR8 ||
+ mt9m001->fourcc == V4L2_PIX_FMT_SBGGR16)
+ /*
+ * Bayer format - even number of rows for simplicity,
+ * but let the user play with the top row.
+ */
+ rect.height = ALIGN(rect.height, 2);
+
+ /* Datasheet requirement: see register description */
+ rect.width = ALIGN(rect.width, 2);
+ rect.left = ALIGN(rect.left, 2);
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
+
+ total_h = rect.height + icd->y_skip_top + vblank;
/* Blanking and start values - default... */
ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
@@ -236,66 +232,126 @@ static int mt9m001_set_crop(struct soc_camera_device *icd,
/* The caller provides a supported format, as verified per
* call to icd->try_fmt() */
if (!ret)
- ret = reg_write(client, MT9M001_COLUMN_START, rect->left);
+ ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
if (!ret)
- ret = reg_write(client, MT9M001_ROW_START, rect->top);
+ ret = reg_write(client, MT9M001_ROW_START, rect.top);
if (!ret)
- ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect->width - 1);
+ ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
if (!ret)
ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
- rect->height + icd->y_skip_top - 1);
+ rect.height + icd->y_skip_top - 1);
if (!ret && mt9m001->autoexposure) {
- ret = reg_write(client, MT9M001_SHUTTER_WIDTH,
- rect->height + icd->y_skip_top + vblank);
+ ret = reg_write(client, MT9M001_SHUTTER_WIDTH, total_h);
if (!ret) {
const struct v4l2_queryctrl *qctrl =
soc_camera_find_qctrl(icd->ops,
V4L2_CID_EXPOSURE);
- icd->exposure = (524 + (rect->height + icd->y_skip_top +
- vblank - 1) *
- (qctrl->maximum - qctrl->minimum)) /
+ mt9m001->exposure = (524 + (total_h - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
1048 + qctrl->minimum;
}
}
+ if (!ret)
+ mt9m001->rect = rect;
+
return ret;
}
-static int mt9m001_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+
+ a->c = mt9m001->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9M001_COLUMN_SKIP;
+ a->bounds.top = MT9M001_ROW_SKIP;
+ a->bounds.width = MT9M001_MAX_WIDTH;
+ a->bounds.height = MT9M001_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9m001_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
- struct v4l2_rect rect = {
- .left = icd->x_current,
- .top = icd->y_current,
- .width = f->fmt.pix.width,
- .height = f->fmt.pix.height,
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ pix->width = mt9m001->rect.width;
+ pix->height = mt9m001->rect.height;
+ pix->pixelformat = mt9m001->fourcc;
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_crop a = {
+ .c = {
+ .left = mt9m001->rect.left,
+ .top = mt9m001->rect.top,
+ .width = pix->width,
+ .height = pix->height,
+ },
};
+ int ret;
/* No support for scaling so far, just crop. TODO: use skipping */
- return mt9m001_set_crop(icd, &rect);
+ ret = mt9m001_s_crop(sd, &a);
+ if (!ret) {
+ pix->width = mt9m001->rect.width;
+ pix->height = mt9m001->rect.height;
+ mt9m001->fourcc = pix->pixelformat;
+ }
+
+ return ret;
}
-static int mt9m001_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
+ struct i2c_client *client = sd->priv;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct v4l2_pix_format *pix = &f->fmt.pix;
- v4l_bound_align_image(&pix->width, 48, 1280, 1,
- &pix->height, 32 + icd->y_skip_top,
- 1024 + icd->y_skip_top, 0, 0);
+ v4l_bound_align_image(&pix->width, MT9M001_MIN_WIDTH,
+ MT9M001_MAX_WIDTH, 1,
+ &pix->height, MT9M001_MIN_HEIGHT + icd->y_skip_top,
+ MT9M001_MAX_HEIGHT + icd->y_skip_top, 0, 0);
+
+ if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
+ pix->pixelformat == V4L2_PIX_FMT_SBGGR16)
+ pix->height = ALIGN(pix->height - 1, 2);
return 0;
}
-static int mt9m001_get_chip_id(struct soc_camera_device *icd,
- struct v4l2_dbg_chip_ident *id)
+static int mt9m001_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
{
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
- if (id->match.addr != mt9m001->client->addr)
+ if (id->match.addr != client->addr)
return -ENODEV;
id->ident = mt9m001->model;
@@ -305,10 +361,10 @@ static int mt9m001_get_chip_id(struct soc_camera_device *icd,
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9m001_get_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9m001_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
return -EINVAL;
@@ -325,10 +381,10 @@ static int mt9m001_get_register(struct soc_camera_device *icd,
return 0;
}
-static int mt9m001_set_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9m001_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
return -EINVAL;
@@ -381,39 +437,17 @@ static const struct v4l2_queryctrl mt9m001_controls[] = {
}
};
-static int mt9m001_video_probe(struct soc_camera_device *);
-static void mt9m001_video_remove(struct soc_camera_device *);
-static int mt9m001_get_control(struct soc_camera_device *, struct v4l2_control *);
-static int mt9m001_set_control(struct soc_camera_device *, struct v4l2_control *);
-
static struct soc_camera_ops mt9m001_ops = {
- .owner = THIS_MODULE,
- .probe = mt9m001_video_probe,
- .remove = mt9m001_video_remove,
- .init = mt9m001_init,
- .release = mt9m001_release,
- .start_capture = mt9m001_start_capture,
- .stop_capture = mt9m001_stop_capture,
- .set_crop = mt9m001_set_crop,
- .set_fmt = mt9m001_set_fmt,
- .try_fmt = mt9m001_try_fmt,
.set_bus_param = mt9m001_set_bus_param,
.query_bus_param = mt9m001_query_bus_param,
.controls = mt9m001_controls,
.num_controls = ARRAY_SIZE(mt9m001_controls),
- .get_control = mt9m001_get_control,
- .set_control = mt9m001_set_control,
- .get_chip_id = mt9m001_get_chip_id,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .get_register = mt9m001_get_register,
- .set_register = mt9m001_set_register,
-#endif
};
-static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+static int mt9m001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
int data;
switch (ctrl->id) {
@@ -426,14 +460,21 @@ static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_contro
case V4L2_CID_EXPOSURE_AUTO:
ctrl->value = mt9m001->autoexposure;
break;
+ case V4L2_CID_GAIN:
+ ctrl->value = mt9m001->gain;
+ break;
+ case V4L2_CID_EXPOSURE:
+ ctrl->value = mt9m001->exposure;
+ break;
}
return 0;
}
-static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
const struct v4l2_queryctrl *qctrl;
int data;
@@ -460,7 +501,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
unsigned long range = qctrl->default_value - qctrl->minimum;
data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
- dev_dbg(&icd->dev, "Setting gain %d\n", data);
+ dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
if (data < 0)
return -EIO;
@@ -478,7 +519,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
else
data = ((gain - 64) * 7 + 28) / 56 + 96;
- dev_dbg(&icd->dev, "Setting gain from %d to %d\n",
+ dev_dbg(&client->dev, "Setting gain from %d to %d\n",
reg_read(client, MT9M001_GLOBAL_GAIN), data);
data = reg_write(client, MT9M001_GLOBAL_GAIN, data);
if (data < 0)
@@ -486,7 +527,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
}
/* Success */
- icd->gain = ctrl->value;
+ mt9m001->gain = ctrl->value;
break;
case V4L2_CID_EXPOSURE:
/* mt9m001 has maximum == default */
@@ -497,23 +538,27 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
unsigned long shutter = ((ctrl->value - qctrl->minimum) * 1048 +
range / 2) / range + 1;
- dev_dbg(&icd->dev, "Setting shutter width from %d to %lu\n",
- reg_read(client, MT9M001_SHUTTER_WIDTH), shutter);
+ dev_dbg(&client->dev,
+ "Setting shutter width from %d to %lu\n",
+ reg_read(client, MT9M001_SHUTTER_WIDTH),
+ shutter);
if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0)
return -EIO;
- icd->exposure = ctrl->value;
+ mt9m001->exposure = ctrl->value;
mt9m001->autoexposure = 0;
}
break;
case V4L2_CID_EXPOSURE_AUTO:
if (ctrl->value) {
const u16 vblank = 25;
- if (reg_write(client, MT9M001_SHUTTER_WIDTH, icd->height +
- icd->y_skip_top + vblank) < 0)
+ unsigned int total_h = mt9m001->rect.height +
+ icd->y_skip_top + vblank;
+ if (reg_write(client, MT9M001_SHUTTER_WIDTH,
+ total_h) < 0)
return -EIO;
qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
- icd->exposure = (524 + (icd->height + icd->y_skip_top + vblank - 1) *
- (qctrl->maximum - qctrl->minimum)) /
+ mt9m001->exposure = (524 + (total_h - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
1048 + qctrl->minimum;
mt9m001->autoexposure = 1;
} else
@@ -525,14 +570,14 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro
/* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one */
-static int mt9m001_video_probe(struct soc_camera_device *icd)
+static int mt9m001_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
s32 data;
- int ret;
unsigned long flags;
+ int ret;
/* We must have a parent by now. And it cannot be a wrong one.
* So this entire test is completely redundant. */
@@ -542,7 +587,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
/* Enable the chip */
data = reg_write(client, MT9M001_CHIP_ENABLE, 1);
- dev_dbg(&icd->dev, "write: %d\n", data);
+ dev_dbg(&client->dev, "write: %d\n", data);
/* Read out the chip version register */
data = reg_read(client, MT9M001_CHIP_VERSION);
@@ -559,10 +604,9 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
icd->formats = mt9m001_monochrome_formats;
break;
default:
- ret = -ENODEV;
- dev_err(&icd->dev,
+ dev_err(&client->dev,
"No MT9M001 chip detected, register read %x\n", data);
- goto ei2c;
+ return -ENODEV;
}
icd->num_formats = 0;
@@ -585,33 +629,57 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
if (flags & SOCAM_DATAWIDTH_8)
icd->num_formats++;
- dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
+ mt9m001->fourcc = icd->formats->fourcc;
+
+ dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
data == 0x8431 ? "C12STM" : "C12ST");
- /* Now that we know the model, we can start video */
- ret = soc_camera_video_start(icd);
- if (ret)
- goto eisis;
+ ret = mt9m001_init(client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to initialise the camera\n");
- return 0;
+ /* mt9m001_init() has reset the chip, returning registers to defaults */
+ mt9m001->gain = 64;
+ mt9m001->exposure = 255;
-eisis:
-ei2c:
return ret;
}
static void mt9m001_video_remove(struct soc_camera_device *icd)
{
- struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
- struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
- dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr,
+ dev_dbg(&icd->dev, "Video removed: %p, %p\n",
icd->dev.parent, icd->vdev);
- soc_camera_video_stop(icd);
if (icl->free_bus)
icl->free_bus(icl);
}
+static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
+ .g_ctrl = mt9m001_g_ctrl,
+ .s_ctrl = mt9m001_s_ctrl,
+ .g_chip_ident = mt9m001_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m001_g_register,
+ .s_register = mt9m001_s_register,
+#endif
+};
+
+static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
+ .s_stream = mt9m001_s_stream,
+ .s_fmt = mt9m001_s_fmt,
+ .g_fmt = mt9m001_g_fmt,
+ .try_fmt = mt9m001_try_fmt,
+ .s_crop = mt9m001_s_crop,
+ .g_crop = mt9m001_g_crop,
+ .cropcap = mt9m001_cropcap,
+};
+
+static struct v4l2_subdev_ops mt9m001_subdev_ops = {
+ .core = &mt9m001_subdev_core_ops,
+ .video = &mt9m001_subdev_video_ops,
+};
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
static int mt9m001_probe(struct i2c_client *client,
const struct i2c_device_id *did)
@@ -620,11 +688,17 @@ static int mt9m001_probe(struct i2c_client *client)
#endif
{
struct mt9m001 *mt9m001;
- struct soc_camera_device *icd;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct soc_camera_link *icl;
int ret;
+ if (!icd) {
+ dev_err(&client->dev, "MT9M001: missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9M001 driver needs platform data\n");
return -EINVAL;
@@ -640,43 +714,40 @@ static int mt9m001_probe(struct i2c_client *client)
if (!mt9m001)
return -ENOMEM;
- mt9m001->client = client;
- i2c_set_clientdata(client, mt9m001);
+ v4l2_i2c_subdev_init(&mt9m001->subdev, client, &mt9m001_subdev_ops);
/* Second stage probe - when a capture adapter is there */
- icd = &mt9m001->icd;
- icd->ops = &mt9m001_ops;
- icd->control = &client->dev;
- icd->x_min = 20;
- icd->y_min = 12;
- icd->x_current = 20;
- icd->y_current = 12;
- icd->width_min = 48;
- icd->width_max = 1280;
- icd->height_min = 32;
- icd->height_max = 1024;
- icd->y_skip_top = 1;
- icd->iface = icl->bus_id;
+ icd->ops = &mt9m001_ops;
+ icd->y_skip_top = 0;
+
+ mt9m001->rect.left = MT9M001_COLUMN_SKIP;
+ mt9m001->rect.top = MT9M001_ROW_SKIP;
+ mt9m001->rect.width = MT9M001_MAX_WIDTH;
+ mt9m001->rect.height = MT9M001_MAX_HEIGHT;
+
/* Simulated autoexposure. If enabled, we calculate shutter width
* ourselves in the driver based on vertical blanking and frame width */
mt9m001->autoexposure = 1;
- ret = soc_camera_device_register(icd);
- if (ret)
- goto eisdr;
-
- return 0;
+ ret = mt9m001_video_probe(icd, client);
+ if (ret) {
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9m001);
+ }
-eisdr:
- kfree(mt9m001);
return ret;
}
static int mt9m001_remove(struct i2c_client *client)
{
- struct mt9m001 *mt9m001 = i2c_get_clientdata(client);
+ struct mt9m001 *mt9m001 = to_mt9m001(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
- soc_camera_device_unregister(&mt9m001->icd);
+ icd->ops = NULL;
+ mt9m001_video_remove(icd);
+ i2c_set_clientdata(client, NULL);
+ client->driver = NULL;
kfree(mt9m001);
return 0;
diff --git a/linux/drivers/media/video/mt9m111.c b/linux/drivers/media/video/mt9m111.c
index 77639ae5c..8171edb72 100644
--- a/linux/drivers/media/video/mt9m111.c
+++ b/linux/drivers/media/video/mt9m111.c
@@ -148,12 +148,12 @@ enum mt9m111_context {
};
struct mt9m111 {
- struct i2c_client *client;
- struct soc_camera_device icd;
+ struct v4l2_subdev subdev;
int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
enum mt9m111_context context;
struct v4l2_rect rect;
u32 pixfmt;
+ unsigned int gain;
unsigned char autoexposure;
unsigned char datawidth;
unsigned int powered:1;
@@ -166,6 +166,11 @@ struct mt9m111 {
unsigned int autowhitebalance:1;
};
+static struct mt9m111 *to_mt9m111(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9m111, subdev);
+}
+
static int reg_page_map_set(struct i2c_client *client, const u16 reg)
{
int ret;
@@ -190,7 +195,7 @@ static int mt9m111_reg_read(struct i2c_client *client, const u16 reg)
ret = reg_page_map_set(client, reg);
if (!ret)
- ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff)));
+ ret = swab16(i2c_smbus_read_word_data(client, reg & 0xff));
dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret);
return ret;
@@ -203,7 +208,7 @@ static int mt9m111_reg_write(struct i2c_client *client, const u16 reg,
ret = reg_page_map_set(client, reg);
if (!ret)
- ret = i2c_smbus_write_word_data(client, (reg & 0xff),
+ ret = i2c_smbus_write_word_data(client, reg & 0xff,
swab16(data));
dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret);
return ret;
@@ -229,10 +234,9 @@ static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg,
return mt9m111_reg_write(client, reg, ret & ~data);
}
-static int mt9m111_set_context(struct soc_camera_device *icd,
+static int mt9m111_set_context(struct i2c_client *client,
enum mt9m111_context ctxt)
{
- struct i2c_client *client = to_i2c_client(icd->control);
int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B
| MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B
| MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B
@@ -246,17 +250,16 @@ static int mt9m111_set_context(struct soc_camera_device *icd,
return reg_write(CONTEXT_CONTROL, valA);
}
-static int mt9m111_setup_rect(struct soc_camera_device *icd,
+static int mt9m111_setup_rect(struct i2c_client *client,
struct v4l2_rect *rect)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret, is_raw_format;
int width = rect->width;
int height = rect->height;
- if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8)
- || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16))
+ if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 ||
+ mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)
is_raw_format = 1;
else
is_raw_format = 0;
@@ -292,9 +295,8 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd,
return ret;
}
-static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt)
+static int mt9m111_setup_pixfmt(struct i2c_client *client, u16 outfmt)
{
- struct i2c_client *client = to_i2c_client(icd->control);
int ret;
ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt);
@@ -303,19 +305,19 @@ static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt)
return ret;
}
-static int mt9m111_setfmt_bayer8(struct soc_camera_device *icd)
+static int mt9m111_setfmt_bayer8(struct i2c_client *client)
{
- return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_PROCESSED_BAYER);
+ return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_PROCESSED_BAYER);
}
-static int mt9m111_setfmt_bayer10(struct soc_camera_device *icd)
+static int mt9m111_setfmt_bayer10(struct i2c_client *client)
{
- return mt9m111_setup_pixfmt(icd, MT9M111_OUTFMT_BYPASS_IFP);
+ return mt9m111_setup_pixfmt(client, MT9M111_OUTFMT_BYPASS_IFP);
}
-static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd)
+static int mt9m111_setfmt_rgb565(struct i2c_client *client)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int val = 0;
if (mt9m111->swap_rgb_red_blue)
@@ -324,12 +326,12 @@ static int mt9m111_setfmt_rgb565(struct soc_camera_device *icd)
val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB565;
- return mt9m111_setup_pixfmt(icd, val);
+ return mt9m111_setup_pixfmt(client, val);
}
-static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd)
+static int mt9m111_setfmt_rgb555(struct i2c_client *client)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int val = 0;
if (mt9m111->swap_rgb_red_blue)
@@ -338,12 +340,12 @@ static int mt9m111_setfmt_rgb555(struct soc_camera_device *icd)
val |= MT9M111_OUTFMT_SWAP_RGB_EVEN;
val |= MT9M111_OUTFMT_RGB | MT9M111_OUTFMT_RGB555;
- return mt9m111_setup_pixfmt(icd, val);
+ return mt9m111_setup_pixfmt(client, val);
}
-static int mt9m111_setfmt_yuv(struct soc_camera_device *icd)
+static int mt9m111_setfmt_yuv(struct i2c_client *client)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int val = 0;
if (mt9m111->swap_yuv_cb_cr)
@@ -351,52 +353,22 @@ static int mt9m111_setfmt_yuv(struct soc_camera_device *icd)
if (mt9m111->swap_yuv_y_chromas)
val |= MT9M111_OUTFMT_SWAP_YCbCr_C_Y;
- return mt9m111_setup_pixfmt(icd, val);
+ return mt9m111_setup_pixfmt(client, val);
}
-static int mt9m111_enable(struct soc_camera_device *icd)
+static int mt9m111_enable(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
- if (icl->power) {
- ret = icl->power(&client->dev, 1);
- if (ret < 0) {
- dev_err(icd->vdev->parent,
- "Platform failed to power-on the camera.\n");
- return ret;
- }
- }
-
ret = reg_set(RESET, MT9M111_RESET_CHIP_ENABLE);
if (!ret)
mt9m111->powered = 1;
return ret;
}
-static int mt9m111_disable(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
- struct soc_camera_link *icl = client->dev.platform_data;
- int ret;
-
- ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE);
- if (!ret)
- mt9m111->powered = 0;
-
- if (icl->power)
- icl->power(&client->dev, 0);
-
- return ret;
-}
-
-static int mt9m111_reset(struct soc_camera_device *icd)
+static int mt9m111_reset(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct soc_camera_link *icl = client->dev.platform_data;
int ret;
ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
@@ -406,26 +378,12 @@ static int mt9m111_reset(struct soc_camera_device *icd)
ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE
| MT9M111_RESET_RESET_SOC);
- if (icl->reset)
- icl->reset(&client->dev);
-
return ret;
}
-static int mt9m111_start_capture(struct soc_camera_device *icd)
-{
- return 0;
-}
-
-static int mt9m111_stop_capture(struct soc_camera_device *icd)
-{
- return 0;
-}
-
static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
- struct soc_camera_link *icl = mt9m111->client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
@@ -438,62 +396,126 @@ static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
return 0;
}
-static int mt9m111_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+static int mt9m111_make_rect(struct i2c_client *client,
+ struct v4l2_rect *rect)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ if (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8 ||
+ mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16) {
+ /* Bayer format - even size lengths */
+ rect->width = ALIGN(rect->width, 2);
+ rect->height = ALIGN(rect->height, 2);
+ /* Let the user play with the starting pixel */
+ }
+
+ /* FIXME: the datasheet doesn't specify minimum sizes */
+ soc_camera_limit_side(&rect->left, &rect->width,
+ MT9M111_MIN_DARK_COLS, 2, MT9M111_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect->top, &rect->height,
+ MT9M111_MIN_DARK_ROWS, 2, MT9M111_MAX_HEIGHT);
+
+ return mt9m111_setup_rect(client, rect);
+}
+
+static int mt9m111_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
- dev_dbg(&icd->dev, "%s left=%d, top=%d, width=%d, height=%d\n",
- __func__, rect->left, rect->top, rect->width,
- rect->height);
+ dev_dbg(&client->dev, "%s left=%d, top=%d, width=%d, height=%d\n",
+ __func__, rect.left, rect.top, rect.width, rect.height);
- ret = mt9m111_setup_rect(icd, rect);
+ ret = mt9m111_make_rect(client, &rect);
if (!ret)
- mt9m111->rect = *rect;
+ mt9m111->rect = rect;
return ret;
}
-static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
+static int mt9m111_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ a->c = mt9m111->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int mt9m111_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9M111_MIN_DARK_COLS;
+ a->bounds.top = MT9M111_MIN_DARK_ROWS;
+ a->bounds.width = MT9M111_MAX_WIDTH;
+ a->bounds.height = MT9M111_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9m111_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ pix->width = mt9m111->rect.width;
+ pix->height = mt9m111->rect.height;
+ pix->pixelformat = mt9m111->pixfmt;
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int mt9m111_set_pixfmt(struct i2c_client *client, u32 pixfmt)
+{
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
switch (pixfmt) {
case V4L2_PIX_FMT_SBGGR8:
- ret = mt9m111_setfmt_bayer8(icd);
+ ret = mt9m111_setfmt_bayer8(client);
break;
case V4L2_PIX_FMT_SBGGR16:
- ret = mt9m111_setfmt_bayer10(icd);
+ ret = mt9m111_setfmt_bayer10(client);
break;
case V4L2_PIX_FMT_RGB555:
- ret = mt9m111_setfmt_rgb555(icd);
+ ret = mt9m111_setfmt_rgb555(client);
break;
case V4L2_PIX_FMT_RGB565:
- ret = mt9m111_setfmt_rgb565(icd);
+ ret = mt9m111_setfmt_rgb565(client);
break;
case V4L2_PIX_FMT_UYVY:
mt9m111->swap_yuv_y_chromas = 0;
mt9m111->swap_yuv_cb_cr = 0;
- ret = mt9m111_setfmt_yuv(icd);
+ ret = mt9m111_setfmt_yuv(client);
break;
case V4L2_PIX_FMT_VYUY:
mt9m111->swap_yuv_y_chromas = 0;
mt9m111->swap_yuv_cb_cr = 1;
- ret = mt9m111_setfmt_yuv(icd);
+ ret = mt9m111_setfmt_yuv(client);
break;
case V4L2_PIX_FMT_YUYV:
mt9m111->swap_yuv_y_chromas = 1;
mt9m111->swap_yuv_cb_cr = 0;
- ret = mt9m111_setfmt_yuv(icd);
+ ret = mt9m111_setfmt_yuv(client);
break;
case V4L2_PIX_FMT_YVYU:
mt9m111->swap_yuv_y_chromas = 1;
mt9m111->swap_yuv_cb_cr = 1;
- ret = mt9m111_setfmt_yuv(icd);
+ ret = mt9m111_setfmt_yuv(client);
break;
default:
- dev_err(&icd->dev, "Pixel format not handled : %x\n", pixfmt);
+ dev_err(&client->dev, "Pixel format not handled : %x\n",
+ pixfmt);
ret = -EINVAL;
}
@@ -503,10 +525,10 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
return ret;
}
-static int mt9m111_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m111_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_rect rect = {
.left = mt9m111->rect.left,
@@ -516,40 +538,56 @@ static int mt9m111_set_fmt(struct soc_camera_device *icd,
};
int ret;
- dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n",
- __func__, pix->pixelformat, rect.left, rect.top, rect.width,
- rect.height);
+ dev_dbg(&client->dev,
+ "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", __func__,
+ pix->pixelformat, rect.left, rect.top, rect.width, rect.height);
- ret = mt9m111_setup_rect(icd, &rect);
+ ret = mt9m111_make_rect(client, &rect);
if (!ret)
- ret = mt9m111_set_pixfmt(icd, pix->pixelformat);
+ ret = mt9m111_set_pixfmt(client, pix->pixelformat);
if (!ret)
mt9m111->rect = rect;
return ret;
}
-static int mt9m111_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9m111_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct v4l2_pix_format *pix = &f->fmt.pix;
+ bool bayer = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
+ pix->pixelformat == V4L2_PIX_FMT_SBGGR16;
+
+ /*
+ * With Bayer format enforce even side lengths, but let the user play
+ * with the starting pixel
+ */
if (pix->height > MT9M111_MAX_HEIGHT)
pix->height = MT9M111_MAX_HEIGHT;
+ else if (pix->height < 2)
+ pix->height = 2;
+ else if (bayer)
+ pix->height = ALIGN(pix->height, 2);
+
if (pix->width > MT9M111_MAX_WIDTH)
pix->width = MT9M111_MAX_WIDTH;
+ else if (pix->width < 2)
+ pix->width = 2;
+ else if (bayer)
+ pix->width = ALIGN(pix->width, 2);
return 0;
}
-static int mt9m111_get_chip_id(struct soc_camera_device *icd,
- struct v4l2_dbg_chip_ident *id)
+static int mt9m111_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
- if (id->match.addr != mt9m111->client->addr)
+ if (id->match.addr != client->addr)
return -ENODEV;
id->ident = mt9m111->model;
@@ -559,11 +597,11 @@ static int mt9m111_get_chip_id(struct soc_camera_device *icd,
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9m111_get_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9m111_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
+ struct i2c_client *client = sd->priv;
int val;
- struct i2c_client *client = to_i2c_client(icd->control);
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
return -EINVAL;
@@ -580,10 +618,10 @@ static int mt9m111_get_register(struct soc_camera_device *icd,
return 0;
}
-static int mt9m111_set_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9m111_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff)
return -EINVAL;
@@ -635,45 +673,21 @@ static const struct v4l2_queryctrl mt9m111_controls[] = {
}
};
-static int mt9m111_video_probe(struct soc_camera_device *);
-static void mt9m111_video_remove(struct soc_camera_device *);
-static int mt9m111_get_control(struct soc_camera_device *,
- struct v4l2_control *);
-static int mt9m111_set_control(struct soc_camera_device *,
- struct v4l2_control *);
static int mt9m111_resume(struct soc_camera_device *icd);
-static int mt9m111_init(struct soc_camera_device *icd);
-static int mt9m111_release(struct soc_camera_device *icd);
+static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state);
static struct soc_camera_ops mt9m111_ops = {
- .owner = THIS_MODULE,
- .probe = mt9m111_video_probe,
- .remove = mt9m111_video_remove,
- .init = mt9m111_init,
+ .suspend = mt9m111_suspend,
.resume = mt9m111_resume,
- .release = mt9m111_release,
- .start_capture = mt9m111_start_capture,
- .stop_capture = mt9m111_stop_capture,
- .set_crop = mt9m111_set_crop,
- .set_fmt = mt9m111_set_fmt,
- .try_fmt = mt9m111_try_fmt,
.query_bus_param = mt9m111_query_bus_param,
.set_bus_param = mt9m111_set_bus_param,
.controls = mt9m111_controls,
.num_controls = ARRAY_SIZE(mt9m111_controls),
- .get_control = mt9m111_get_control,
- .set_control = mt9m111_set_control,
- .get_chip_id = mt9m111_get_chip_id,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .get_register = mt9m111_get_register,
- .set_register = mt9m111_set_register,
-#endif
};
-static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask)
+static int mt9m111_set_flip(struct i2c_client *client, int flip, int mask)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
if (mt9m111->context == HIGHPOWER) {
@@ -691,9 +705,8 @@ static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask)
return ret;
}
-static int mt9m111_get_global_gain(struct soc_camera_device *icd)
+static int mt9m111_get_global_gain(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
int data;
data = reg_read(GLOBAL_GAIN);
@@ -703,15 +716,15 @@ static int mt9m111_get_global_gain(struct soc_camera_device *icd)
return data;
}
-static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain)
+static int mt9m111_set_global_gain(struct i2c_client *client, int gain)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
u16 val;
if (gain > 63 * 2 * 2)
return -EINVAL;
- icd->gain = gain;
+ mt9m111->gain = gain;
if ((gain >= 64 * 2) && (gain < 63 * 2 * 2))
val = (1 << 10) | (1 << 9) | (gain / 4);
else if ((gain >= 64) && (gain < 64 * 2))
@@ -722,10 +735,9 @@ static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain)
return reg_write(GLOBAL_GAIN, val);
}
-static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on)
+static int mt9m111_set_autoexposure(struct i2c_client *client, int on)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
if (on)
@@ -739,10 +751,9 @@ static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on)
return ret;
}
-static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on)
+static int mt9m111_set_autowhitebalance(struct i2c_client *client, int on)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
if (on)
@@ -756,11 +767,10 @@ static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on)
return ret;
}
-static int mt9m111_get_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
+static int mt9m111_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int data;
switch (ctrl->id) {
@@ -785,7 +795,7 @@ static int mt9m111_get_control(struct soc_camera_device *icd,
ctrl->value = !!(data & MT9M111_RMB_MIRROR_COLS);
break;
case V4L2_CID_GAIN:
- data = mt9m111_get_global_gain(icd);
+ data = mt9m111_get_global_gain(client);
if (data < 0)
return data;
ctrl->value = data;
@@ -800,37 +810,36 @@ static int mt9m111_get_control(struct soc_camera_device *icd,
return 0;
}
-static int mt9m111_set_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
+static int mt9m111_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
const struct v4l2_queryctrl *qctrl;
int ret;
qctrl = soc_camera_find_qctrl(&mt9m111_ops, ctrl->id);
-
if (!qctrl)
return -EINVAL;
switch (ctrl->id) {
case V4L2_CID_VFLIP:
mt9m111->vflip = ctrl->value;
- ret = mt9m111_set_flip(icd, ctrl->value,
+ ret = mt9m111_set_flip(client, ctrl->value,
MT9M111_RMB_MIRROR_ROWS);
break;
case V4L2_CID_HFLIP:
mt9m111->hflip = ctrl->value;
- ret = mt9m111_set_flip(icd, ctrl->value,
+ ret = mt9m111_set_flip(client, ctrl->value,
MT9M111_RMB_MIRROR_COLS);
break;
case V4L2_CID_GAIN:
- ret = mt9m111_set_global_gain(icd, ctrl->value);
+ ret = mt9m111_set_global_gain(client, ctrl->value);
break;
case V4L2_CID_EXPOSURE_AUTO:
- ret = mt9m111_set_autoexposure(icd, ctrl->value);
+ ret = mt9m111_set_autoexposure(client, ctrl->value);
break;
case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = mt9m111_set_autowhitebalance(icd, ctrl->value);
+ ret = mt9m111_set_autowhitebalance(client, ctrl->value);
break;
default:
ret = -EINVAL;
@@ -839,62 +848,62 @@ static int mt9m111_set_control(struct soc_camera_device *icd,
return ret;
}
-static int mt9m111_restore_state(struct soc_camera_device *icd)
+static int mt9m111_suspend(struct soc_camera_device *icd, pm_message_t state)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
-
- mt9m111_set_context(icd, mt9m111->context);
- mt9m111_set_pixfmt(icd, mt9m111->pixfmt);
- mt9m111_setup_rect(icd, &mt9m111->rect);
- mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
- mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
- mt9m111_set_global_gain(icd, icd->gain);
- mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
- mt9m111_set_autowhitebalance(icd, mt9m111->autowhitebalance);
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ mt9m111->gain = mt9m111_get_global_gain(client);
+
+ return 0;
+}
+
+static int mt9m111_restore_state(struct i2c_client *client)
+{
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+
+ mt9m111_set_context(client, mt9m111->context);
+ mt9m111_set_pixfmt(client, mt9m111->pixfmt);
+ mt9m111_setup_rect(client, &mt9m111->rect);
+ mt9m111_set_flip(client, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
+ mt9m111_set_flip(client, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
+ mt9m111_set_global_gain(client, mt9m111->gain);
+ mt9m111_set_autoexposure(client, mt9m111->autoexposure);
+ mt9m111_set_autowhitebalance(client, mt9m111->autowhitebalance);
return 0;
}
static int mt9m111_resume(struct soc_camera_device *icd)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret = 0;
if (mt9m111->powered) {
- ret = mt9m111_enable(icd);
+ ret = mt9m111_enable(client);
if (!ret)
- ret = mt9m111_reset(icd);
+ ret = mt9m111_reset(client);
if (!ret)
- ret = mt9m111_restore_state(icd);
+ ret = mt9m111_restore_state(client);
}
return ret;
}
-static int mt9m111_init(struct soc_camera_device *icd)
+static int mt9m111_init(struct i2c_client *client)
{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
int ret;
mt9m111->context = HIGHPOWER;
- ret = mt9m111_enable(icd);
+ ret = mt9m111_enable(client);
if (!ret)
- ret = mt9m111_reset(icd);
+ ret = mt9m111_reset(client);
if (!ret)
- ret = mt9m111_set_context(icd, mt9m111->context);
+ ret = mt9m111_set_context(client, mt9m111->context);
if (!ret)
- ret = mt9m111_set_autoexposure(icd, mt9m111->autoexposure);
+ ret = mt9m111_set_autoexposure(client, mt9m111->autoexposure);
if (ret)
- dev_err(&icd->dev, "mt9m11x init failed: %d\n", ret);
- return ret;
-}
-
-static int mt9m111_release(struct soc_camera_device *icd)
-{
- int ret;
-
- ret = mt9m111_disable(icd);
- if (ret < 0)
- dev_err(&icd->dev, "mt9m11x release failed: %d\n", ret);
-
+ dev_err(&client->dev, "mt9m11x init failed: %d\n", ret);
return ret;
}
@@ -902,10 +911,10 @@ static int mt9m111_release(struct soc_camera_device *icd)
* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one
*/
-static int mt9m111_video_probe(struct soc_camera_device *icd)
+static int mt9m111_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
s32 data;
int ret;
@@ -917,10 +926,13 @@ static int mt9m111_video_probe(struct soc_camera_device *icd)
to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
return -ENODEV;
- ret = mt9m111_enable(icd);
- if (ret)
- goto ei2c;
- ret = mt9m111_reset(icd);
+ mt9m111->autoexposure = 1;
+ mt9m111->autowhitebalance = 1;
+
+ mt9m111->swap_rgb_even_odd = 1;
+ mt9m111->swap_rgb_red_blue = 1;
+
+ ret = mt9m111_init(client);
if (ret)
goto ei2c;
@@ -935,7 +947,7 @@ static int mt9m111_video_probe(struct soc_camera_device *icd)
break;
default:
ret = -ENODEV;
- dev_err(&icd->dev,
+ dev_err(&client->dev,
"No MT9M11x chip detected, register read %x\n", data);
goto ei2c;
}
@@ -943,32 +955,35 @@ static int mt9m111_video_probe(struct soc_camera_device *icd)
icd->formats = mt9m111_colour_formats;
icd->num_formats = ARRAY_SIZE(mt9m111_colour_formats);
- dev_info(&icd->dev, "Detected a MT9M11x chip ID %x\n", data);
+ dev_info(&client->dev, "Detected a MT9M11x chip ID %x\n", data);
- ret = soc_camera_video_start(icd);
- if (ret)
- goto eisis;
-
- mt9m111->autoexposure = 1;
- mt9m111->autowhitebalance = 1;
-
- mt9m111->swap_rgb_even_odd = 1;
- mt9m111->swap_rgb_red_blue = 1;
-
- return 0;
-eisis:
ei2c:
return ret;
}
-static void mt9m111_video_remove(struct soc_camera_device *icd)
-{
- struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+static struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = {
+ .g_ctrl = mt9m111_g_ctrl,
+ .s_ctrl = mt9m111_s_ctrl,
+ .g_chip_ident = mt9m111_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m111_g_register,
+ .s_register = mt9m111_s_register,
+#endif
+};
- dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m111->client->addr,
- mt9m111->icd.dev.parent, mt9m111->icd.vdev);
- soc_camera_video_stop(&mt9m111->icd);
-}
+static struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = {
+ .s_fmt = mt9m111_s_fmt,
+ .g_fmt = mt9m111_g_fmt,
+ .try_fmt = mt9m111_try_fmt,
+ .s_crop = mt9m111_s_crop,
+ .g_crop = mt9m111_g_crop,
+ .cropcap = mt9m111_cropcap,
+};
+
+static struct v4l2_subdev_ops mt9m111_subdev_ops = {
+ .core = &mt9m111_subdev_core_ops,
+ .video = &mt9m111_subdev_video_ops,
+};
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
static int mt9m111_probe(struct i2c_client *client)
@@ -978,11 +993,17 @@ static int mt9m111_probe(struct i2c_client *client,
#endif
{
struct mt9m111 *mt9m111;
- struct soc_camera_device *icd;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct soc_camera_link *icl;
int ret;
+ if (!icd) {
+ dev_err(&client->dev, "MT9M11x: missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9M11x driver needs platform data\n");
return -EINVAL;
@@ -998,38 +1019,35 @@ static int mt9m111_probe(struct i2c_client *client,
if (!mt9m111)
return -ENOMEM;
- mt9m111->client = client;
- i2c_set_clientdata(client, mt9m111);
+ v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops);
/* Second stage probe - when a capture adapter is there */
- icd = &mt9m111->icd;
- icd->ops = &mt9m111_ops;
- icd->control = &client->dev;
- icd->x_min = MT9M111_MIN_DARK_COLS;
- icd->y_min = MT9M111_MIN_DARK_ROWS;
- icd->x_current = icd->x_min;
- icd->y_current = icd->y_min;
- icd->width_min = MT9M111_MIN_DARK_ROWS;
- icd->width_max = MT9M111_MAX_WIDTH;
- icd->height_min = MT9M111_MIN_DARK_COLS;
- icd->height_max = MT9M111_MAX_HEIGHT;
- icd->y_skip_top = 0;
- icd->iface = icl->bus_id;
-
- ret = soc_camera_device_register(icd);
- if (ret)
- goto eisdr;
- return 0;
+ icd->ops = &mt9m111_ops;
+ icd->y_skip_top = 0;
+
+ mt9m111->rect.left = MT9M111_MIN_DARK_COLS;
+ mt9m111->rect.top = MT9M111_MIN_DARK_ROWS;
+ mt9m111->rect.width = MT9M111_MAX_WIDTH;
+ mt9m111->rect.height = MT9M111_MAX_HEIGHT;
+
+ ret = mt9m111_video_probe(icd, client);
+ if (ret) {
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9m111);
+ }
-eisdr:
- kfree(mt9m111);
return ret;
}
static int mt9m111_remove(struct i2c_client *client)
{
- struct mt9m111 *mt9m111 = i2c_get_clientdata(client);
- soc_camera_device_unregister(&mt9m111->icd);
+ struct mt9m111 *mt9m111 = to_mt9m111(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
+
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ client->driver = NULL;
kfree(mt9m111);
return 0;
diff --git a/linux/drivers/media/video/mt9t031.c b/linux/drivers/media/video/mt9t031.c
index a57051522..bf3b54f9f 100644
--- a/linux/drivers/media/video/mt9t031.c
+++ b/linux/drivers/media/video/mt9t031.c
@@ -13,13 +13,13 @@
#include <linux/i2c.h>
#include <linux/log2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
/* mt9t031 i2c address 0x5d
- * The platform has to define i2c_board_info
- * and call i2c_register_board_info() */
+ * The platform has to define i2c_board_info and link to it from
+ * struct soc_camera_link */
/* mt9t031 selected register addresses */
#define MT9T031_CHIP_VERSION 0x00
@@ -47,7 +47,7 @@
#define MT9T031_MAX_HEIGHT 1536
#define MT9T031_MAX_WIDTH 2048
#define MT9T031_MIN_HEIGHT 2
-#define MT9T031_MIN_WIDTH 2
+#define MT9T031_MIN_WIDTH 18
#define MT9T031_HORIZONTAL_BLANK 142
#define MT9T031_VERTICAL_BLANK 25
#define MT9T031_COLUMN_SKIP 32
@@ -68,14 +68,21 @@ static const struct soc_camera_data_format mt9t031_colour_formats[] = {
};
struct mt9t031 {
- struct i2c_client *client;
- struct soc_camera_device icd;
+ struct v4l2_subdev subdev;
+ struct v4l2_rect rect; /* Sensor window */
int model; /* V4L2_IDENT_MT9T031* codes from v4l2-chip-ident.h */
- unsigned char autoexposure;
u16 xskip;
u16 yskip;
+ unsigned int gain;
+ unsigned int exposure;
+ unsigned char autoexposure;
};
+static struct mt9t031 *to_mt9t031(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9t031, subdev);
+}
+
static int reg_read(struct i2c_client *client, const u8 reg)
{
s32 data = i2c_smbus_read_word_data(client, reg);
@@ -136,21 +143,10 @@ static int get_shutter(struct i2c_client *client, u32 *data)
return ret < 0 ? ret : 0;
}
-static int mt9t031_init(struct soc_camera_device *icd)
+static int mt9t031_idle(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct soc_camera_link *icl = client->dev.platform_data;
int ret;
- if (icl->power) {
- ret = icl->power(&client->dev, 1);
- if (ret < 0) {
- dev_err(icd->vdev->parent,
- "Platform failed to power-on the camera.\n");
- return ret;
- }
- }
-
/* Disable chip output, synchronous option update */
ret = reg_write(client, MT9T031_RESET, 1);
if (ret >= 0)
@@ -158,50 +154,39 @@ static int mt9t031_init(struct soc_camera_device *icd)
if (ret >= 0)
ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
- if (ret < 0 && icl->power)
- icl->power(&client->dev, 0);
-
return ret >= 0 ? 0 : -EIO;
}
-static int mt9t031_release(struct soc_camera_device *icd)
+static int mt9t031_disable(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct soc_camera_link *icl = client->dev.platform_data;
-
/* Disable the chip */
reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
- if (icl->power)
- icl->power(&client->dev, 0);
-
return 0;
}
-static int mt9t031_start_capture(struct soc_camera_device *icd)
+static int mt9t031_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct i2c_client *client = to_i2c_client(icd->control);
-
- /* Switch to master "normal" mode */
- if (reg_set(client, MT9T031_OUTPUT_CONTROL, 2) < 0)
- return -EIO;
- return 0;
-}
+ struct i2c_client *client = sd->priv;
+ int ret;
-static int mt9t031_stop_capture(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(icd->control);
+ if (enable)
+ /* Switch to master "normal" mode */
+ ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 2);
+ else
+ /* Stop sensor readout */
+ ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2);
- /* Stop sensor readout */
- if (reg_clear(client, MT9T031_OUTPUT_CONTROL, 2) < 0)
+ if (ret < 0)
return -EIO;
+
return 0;
}
static int mt9t031_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
/* The caller should have queried our parameters, check anyway */
if (flags & ~MT9T031_BUS_PARAM)
@@ -217,69 +202,73 @@ static int mt9t031_set_bus_param(struct soc_camera_device *icd,
static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
{
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
- struct soc_camera_link *icl = mt9t031->client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
}
-/* Round up minima and round down maxima */
-static void recalculate_limits(struct soc_camera_device *icd,
- u16 xskip, u16 yskip)
+/* target must be _even_ */
+static u16 mt9t031_skip(s32 *source, s32 target, s32 max)
{
- icd->x_min = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip;
- icd->y_min = (MT9T031_ROW_SKIP + yskip - 1) / yskip;
- icd->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip;
- icd->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip;
- icd->width_max = MT9T031_MAX_WIDTH / xskip;
- icd->height_max = MT9T031_MAX_HEIGHT / yskip;
+ unsigned int skip;
+
+ if (*source < target + target / 2) {
+ *source = target;
+ return 1;
+ }
+
+ skip = min(max, *source + target / 2) / target;
+ if (skip > 8)
+ skip = 8;
+ *source = target * skip;
+
+ return skip;
}
+/* rect is the sensor rectangle, the caller guarantees parameter validity */
static int mt9t031_set_params(struct soc_camera_device *icd,
struct v4l2_rect *rect, u16 xskip, u16 yskip)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
int ret;
- u16 xbin, ybin, width, height, left, top;
+ u16 xbin, ybin;
const u16 hblank = MT9T031_HORIZONTAL_BLANK,
vblank = MT9T031_VERTICAL_BLANK;
- /* Make sure we don't exceed sensor limits */
- if (rect->left + rect->width > icd->width_max)
- rect->left = (icd->width_max - rect->width) / 2 + icd->x_min;
-
- if (rect->top + rect->height > icd->height_max)
- rect->top = (icd->height_max - rect->height) / 2 + icd->y_min;
-
- width = rect->width * xskip;
- height = rect->height * yskip;
- left = rect->left * xskip;
- top = rect->top * yskip;
-
xbin = min(xskip, (u16)3);
ybin = min(yskip, (u16)3);
- dev_dbg(&icd->dev, "xskip %u, width %u/%u, yskip %u, height %u/%u\n",
- xskip, width, rect->width, yskip, height, rect->height);
-
- /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */
+ /*
+ * Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper.
+ * There is always a valid suitably aligned value. The worst case is
+ * xbin = 3, width = 2048. Then we will start at 36, the last read out
+ * pixel will be 2083, which is < 2085 - first black pixel.
+ *
+ * MT9T031 datasheet imposes window left border alignment, depending on
+ * the selected xskip. Failing to conform to this requirement produces
+ * dark horizontal stripes in the image. However, even obeying to this
+ * requirement doesn't eliminate the stripes in all configurations. They
+ * appear "locally reproducibly," but can differ between tests under
+ * different lighting conditions.
+ */
switch (xbin) {
- case 2:
- left = (left + 3) & ~3;
+ case 1:
+ rect->left &= ~1;
break;
- case 3:
- left = roundup(left, 6);
- }
-
- switch (ybin) {
case 2:
- top = (top + 3) & ~3;
+ rect->left &= ~3;
break;
case 3:
- top = roundup(top, 6);
+ rect->left = rect->left > roundup(MT9T031_COLUMN_SKIP, 6) ?
+ (rect->left / 6) * 6 : roundup(MT9T031_COLUMN_SKIP, 6);
}
+ rect->top &= ~1;
+
+ dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n",
+ xskip, yskip, rect->width, rect->height, rect->left, rect->top);
+
/* Disable register update, reconfigure atomically */
ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1);
if (ret < 0)
@@ -299,29 +288,30 @@ static int mt9t031_set_params(struct soc_camera_device *icd,
ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE,
((ybin - 1) << 4) | (yskip - 1));
}
- dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, top);
+ dev_dbg(&client->dev, "new physical left %u, top %u\n",
+ rect->left, rect->top);
/* The caller provides a supported format, as guaranteed by
* icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */
if (ret >= 0)
- ret = reg_write(client, MT9T031_COLUMN_START, left);
+ ret = reg_write(client, MT9T031_COLUMN_START, rect->left);
if (ret >= 0)
- ret = reg_write(client, MT9T031_ROW_START, top);
+ ret = reg_write(client, MT9T031_ROW_START, rect->top);
if (ret >= 0)
- ret = reg_write(client, MT9T031_WINDOW_WIDTH, width - 1);
+ ret = reg_write(client, MT9T031_WINDOW_WIDTH, rect->width - 1);
if (ret >= 0)
ret = reg_write(client, MT9T031_WINDOW_HEIGHT,
- height + icd->y_skip_top - 1);
+ rect->height + icd->y_skip_top - 1);
if (ret >= 0 && mt9t031->autoexposure) {
- ret = set_shutter(client, height + icd->y_skip_top + vblank);
+ unsigned int total_h = rect->height + icd->y_skip_top + vblank;
+ ret = set_shutter(client, total_h);
if (ret >= 0) {
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
const struct v4l2_queryctrl *qctrl =
soc_camera_find_qctrl(icd->ops,
V4L2_CID_EXPOSURE);
- icd->exposure = (shutter_max / 2 + (height +
- icd->y_skip_top + vblank - 1) *
- (qctrl->maximum - qctrl->minimum)) /
+ mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
shutter_max + qctrl->minimum;
}
}
@@ -330,58 +320,99 @@ static int mt9t031_set_params(struct soc_camera_device *icd,
if (ret >= 0)
ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1);
+ if (ret >= 0) {
+ mt9t031->rect = *rect;
+ mt9t031->xskip = xskip;
+ mt9t031->yskip = yskip;
+ }
+
return ret < 0 ? ret : 0;
}
-static int mt9t031_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+static int mt9t031_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct v4l2_rect rect = a->c;
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
+
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9T031_COLUMN_SKIP, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9T031_ROW_SKIP, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT);
- /* CROP - no change in scaling, or in limits */
- return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip);
+ return mt9t031_set_params(icd, &rect, mt9t031->xskip, mt9t031->yskip);
}
-static int mt9t031_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9t031_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
- int ret;
- u16 xskip, yskip;
- struct v4l2_rect rect = {
- .left = icd->x_current,
- .top = icd->y_current,
- .width = f->fmt.pix.width,
- .height = f->fmt.pix.height,
- };
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
- /*
- * try_fmt has put rectangle within limits.
- * S_FMT - use binning and skipping for scaling, recalculate
- * limits, used for cropping
- */
- /* Is this more optimal than just a division? */
- for (xskip = 8; xskip > 1; xskip--)
- if (rect.width * xskip <= MT9T031_MAX_WIDTH)
- break;
+ a->c = mt9t031->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- for (yskip = 8; yskip > 1; yskip--)
- if (rect.height * yskip <= MT9T031_MAX_HEIGHT)
- break;
+ return 0;
+}
- recalculate_limits(icd, xskip, yskip);
+static int mt9t031_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = MT9T031_COLUMN_SKIP;
+ a->bounds.top = MT9T031_ROW_SKIP;
+ a->bounds.width = MT9T031_MAX_WIDTH;
+ a->bounds.height = MT9T031_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
- ret = mt9t031_set_params(icd, &rect, xskip, yskip);
- if (!ret) {
- mt9t031->xskip = xskip;
- mt9t031->yskip = yskip;
- }
+ return 0;
+}
- return ret;
+static int mt9t031_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ pix->width = mt9t031->rect.width / mt9t031->xskip;
+ pix->height = mt9t031->rect.height / mt9t031->yskip;
+ pix->pixelformat = V4L2_PIX_FMT_SGRBG10;
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int mt9t031_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ u16 xskip, yskip;
+ struct v4l2_rect rect = mt9t031->rect;
+
+ /*
+ * try_fmt has put width and height within limits.
+ * S_FMT: use binning and skipping for scaling
+ */
+ xskip = mt9t031_skip(&rect.width, pix->width, MT9T031_MAX_WIDTH);
+ yskip = mt9t031_skip(&rect.height, pix->height, MT9T031_MAX_HEIGHT);
+
+ /* mt9t031_set_params() doesn't change width and height */
+ return mt9t031_set_params(icd, &rect, xskip, yskip);
}
-static int mt9t031_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+/*
+ * If a user window larger than sensor window is requested, we'll increase the
+ * sensor window.
+ */
+static int mt9t031_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
struct v4l2_pix_format *pix = &f->fmt.pix;
@@ -392,15 +423,16 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd,
return 0;
}
-static int mt9t031_get_chip_id(struct soc_camera_device *icd,
- struct v4l2_dbg_chip_ident *id)
+static int mt9t031_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
{
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
- if (id->match.addr != mt9t031->client->addr)
+ if (id->match.addr != client->addr)
return -ENODEV;
id->ident = mt9t031->model;
@@ -410,10 +442,10 @@ static int mt9t031_get_chip_id(struct soc_camera_device *icd,
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9t031_get_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9t031_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
return -EINVAL;
@@ -429,10 +461,10 @@ static int mt9t031_get_register(struct soc_camera_device *icd,
return 0;
}
-static int mt9t031_set_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9t031_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
return -EINVAL;
@@ -493,39 +525,17 @@ static const struct v4l2_queryctrl mt9t031_controls[] = {
}
};
-static int mt9t031_video_probe(struct soc_camera_device *);
-static void mt9t031_video_remove(struct soc_camera_device *);
-static int mt9t031_get_control(struct soc_camera_device *, struct v4l2_control *);
-static int mt9t031_set_control(struct soc_camera_device *, struct v4l2_control *);
-
static struct soc_camera_ops mt9t031_ops = {
- .owner = THIS_MODULE,
- .probe = mt9t031_video_probe,
- .remove = mt9t031_video_remove,
- .init = mt9t031_init,
- .release = mt9t031_release,
- .start_capture = mt9t031_start_capture,
- .stop_capture = mt9t031_stop_capture,
- .set_crop = mt9t031_set_crop,
- .set_fmt = mt9t031_set_fmt,
- .try_fmt = mt9t031_try_fmt,
.set_bus_param = mt9t031_set_bus_param,
.query_bus_param = mt9t031_query_bus_param,
.controls = mt9t031_controls,
.num_controls = ARRAY_SIZE(mt9t031_controls),
- .get_control = mt9t031_get_control,
- .set_control = mt9t031_set_control,
- .get_chip_id = mt9t031_get_chip_id,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .get_register = mt9t031_get_register,
- .set_register = mt9t031_set_register,
-#endif
};
-static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+static int mt9t031_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
int data;
switch (ctrl->id) {
@@ -544,14 +554,21 @@ static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_contro
case V4L2_CID_EXPOSURE_AUTO:
ctrl->value = mt9t031->autoexposure;
break;
+ case V4L2_CID_GAIN:
+ ctrl->value = mt9t031->gain;
+ break;
+ case V4L2_CID_EXPOSURE:
+ ctrl->value = mt9t031->exposure;
+ break;
}
return 0;
}
-static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl)
+static int mt9t031_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
const struct v4l2_queryctrl *qctrl;
int data;
@@ -586,7 +603,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro
unsigned long range = qctrl->default_value - qctrl->minimum;
data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range;
- dev_dbg(&icd->dev, "Setting gain %d\n", data);
+ dev_dbg(&client->dev, "Setting gain %d\n", data);
data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
if (data < 0)
return -EIO;
@@ -606,7 +623,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro
/* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */
data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60;
- dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n",
+ dev_dbg(&client->dev, "Set gain from 0x%x to 0x%x\n",
reg_read(client, MT9T031_GLOBAL_GAIN), data);
data = reg_write(client, MT9T031_GLOBAL_GAIN, data);
if (data < 0)
@@ -614,7 +631,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro
}
/* Success */
- icd->gain = ctrl->value;
+ mt9t031->gain = ctrl->value;
break;
case V4L2_CID_EXPOSURE:
/* mt9t031 has maximum == default */
@@ -627,11 +644,11 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro
u32 old;
get_shutter(client, &old);
- dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n",
+ dev_dbg(&client->dev, "Set shutter from %u to %u\n",
old, shutter);
if (set_shutter(client, shutter) < 0)
return -EIO;
- icd->exposure = ctrl->value;
+ mt9t031->exposure = ctrl->value;
mt9t031->autoexposure = 0;
}
break;
@@ -639,13 +656,14 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro
if (ctrl->value) {
const u16 vblank = MT9T031_VERTICAL_BLANK;
const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank;
- if (set_shutter(client, icd->height +
- icd->y_skip_top + vblank) < 0)
+ unsigned int total_h = mt9t031->rect.height +
+ icd->y_skip_top + vblank;
+
+ if (set_shutter(client, total_h) < 0)
return -EIO;
qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
- icd->exposure = (shutter_max / 2 + (icd->height +
- icd->y_skip_top + vblank - 1) *
- (qctrl->maximum - qctrl->minimum)) /
+ mt9t031->exposure = (shutter_max / 2 + (total_h - 1) *
+ (qctrl->maximum - qctrl->minimum)) /
shutter_max + qctrl->minimum;
mt9t031->autoexposure = 1;
} else
@@ -657,22 +675,16 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro
/* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one */
-static int mt9t031_video_probe(struct soc_camera_device *icd)
+static int mt9t031_video_probe(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
s32 data;
int ret;
- /* We must have a parent by now. And it cannot be a wrong one.
- * So this entire test is completely redundant. */
- if (!icd->dev.parent ||
- to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
- return -ENODEV;
-
/* Enable the chip */
data = reg_write(client, MT9T031_CHIP_ENABLE, 1);
- dev_dbg(&icd->dev, "write: %d\n", data);
+ dev_dbg(&client->dev, "write: %d\n", data);
/* Read out the chip version register */
data = reg_read(client, MT9T031_CHIP_VERSION);
@@ -684,34 +696,48 @@ static int mt9t031_video_probe(struct soc_camera_device *icd)
icd->num_formats = ARRAY_SIZE(mt9t031_colour_formats);
break;
default:
- ret = -ENODEV;
- dev_err(&icd->dev,
+ dev_err(&client->dev,
"No MT9T031 chip detected, register read %x\n", data);
- goto ei2c;
+ return -ENODEV;
}
- dev_info(&icd->dev, "Detected a MT9T031 chip ID %x\n", data);
+ dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data);
- /* Now that we know the model, we can start video */
- ret = soc_camera_video_start(icd);
- if (ret)
- goto evstart;
+ ret = mt9t031_idle(client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to initialise the camera\n");
- return 0;
+ /* mt9t031_idle() has reset the chip to default. */
+ mt9t031->exposure = 255;
+ mt9t031->gain = 64;
-evstart:
-ei2c:
return ret;
}
-static void mt9t031_video_remove(struct soc_camera_device *icd)
-{
- struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = {
+ .g_ctrl = mt9t031_g_ctrl,
+ .s_ctrl = mt9t031_s_ctrl,
+ .g_chip_ident = mt9t031_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9t031_g_register,
+ .s_register = mt9t031_s_register,
+#endif
+};
- dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9t031->client->addr,
- icd->dev.parent, icd->vdev);
- soc_camera_video_stop(icd);
-}
+static struct v4l2_subdev_video_ops mt9t031_subdev_video_ops = {
+ .s_stream = mt9t031_s_stream,
+ .s_fmt = mt9t031_s_fmt,
+ .g_fmt = mt9t031_g_fmt,
+ .try_fmt = mt9t031_try_fmt,
+ .s_crop = mt9t031_s_crop,
+ .g_crop = mt9t031_g_crop,
+ .cropcap = mt9t031_cropcap,
+};
+
+static struct v4l2_subdev_ops mt9t031_subdev_ops = {
+ .core = &mt9t031_subdev_core_ops,
+ .video = &mt9t031_subdev_video_ops,
+};
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
static int mt9t031_probe(struct i2c_client *client)
@@ -721,11 +747,17 @@ static int mt9t031_probe(struct i2c_client *client,
#endif
{
struct mt9t031 *mt9t031;
- struct soc_camera_device *icd;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct soc_camera_link *icl;
int ret;
+ if (!icd) {
+ dev_err(&client->dev, "MT9T031: missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9T031 driver needs platform data\n");
return -EINVAL;
@@ -741,23 +773,17 @@ static int mt9t031_probe(struct i2c_client *client,
if (!mt9t031)
return -ENOMEM;
- mt9t031->client = client;
- i2c_set_clientdata(client, mt9t031);
+ v4l2_i2c_subdev_init(&mt9t031->subdev, client, &mt9t031_subdev_ops);
/* Second stage probe - when a capture adapter is there */
- icd = &mt9t031->icd;
- icd->ops = &mt9t031_ops;
- icd->control = &client->dev;
- icd->x_min = MT9T031_COLUMN_SKIP;
- icd->y_min = MT9T031_ROW_SKIP;
- icd->x_current = icd->x_min;
- icd->y_current = icd->y_min;
- icd->width_min = MT9T031_MIN_WIDTH;
- icd->width_max = MT9T031_MAX_WIDTH;
- icd->height_min = MT9T031_MIN_HEIGHT;
- icd->height_max = MT9T031_MAX_HEIGHT;
- icd->y_skip_top = 0;
- icd->iface = icl->bus_id;
+ icd->ops = &mt9t031_ops;
+ icd->y_skip_top = 0;
+
+ mt9t031->rect.left = MT9T031_COLUMN_SKIP;
+ mt9t031->rect.top = MT9T031_ROW_SKIP;
+ mt9t031->rect.width = MT9T031_MAX_WIDTH;
+ mt9t031->rect.height = MT9T031_MAX_HEIGHT;
+
/* Simulated autoexposure. If enabled, we calculate shutter width
* ourselves in the driver based on vertical blanking and frame width */
mt9t031->autoexposure = 1;
@@ -765,24 +791,29 @@ static int mt9t031_probe(struct i2c_client *client,
mt9t031->xskip = 1;
mt9t031->yskip = 1;
- ret = soc_camera_device_register(icd);
- if (ret)
- goto eisdr;
+ mt9t031_idle(client);
- return 0;
+ ret = mt9t031_video_probe(client);
+
+ mt9t031_disable(client);
+
+ if (ret) {
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9t031);
+ }
-eisdr:
- i2c_set_clientdata(client, NULL);
- kfree(mt9t031);
return ret;
}
static int mt9t031_remove(struct i2c_client *client)
{
- struct mt9t031 *mt9t031 = i2c_get_clientdata(client);
+ struct mt9t031 *mt9t031 = to_mt9t031(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
- soc_camera_device_unregister(&mt9t031->icd);
+ icd->ops = NULL;
i2c_set_clientdata(client, NULL);
+ client->driver = NULL;
kfree(mt9t031);
return 0;
diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c
index 4841e6eea..9da3dde10 100644
--- a/linux/drivers/media/video/mt9v022.c
+++ b/linux/drivers/media/video/mt9v022.c
@@ -14,13 +14,13 @@
#include <linux/delay.h>
#include <linux/log2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
#include <media/v4l2-chip-ident.h>
#include <media/soc_camera.h>
/* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c
- * The platform has to define i2c_board_info
- * and call i2c_register_board_info() */
+ * The platform has to define ctruct i2c_board_info objects and link to them
+ * from struct soc_camera_link */
static char *sensor_type;
module_param(sensor_type, charp, S_IRUGO);
@@ -45,7 +45,7 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
#define MT9V022_PIXEL_OPERATION_MODE 0x0f
#define MT9V022_LED_OUT_CONTROL 0x1b
#define MT9V022_ADC_MODE_CONTROL 0x1c
-#define MT9V022_ANALOG_GAIN 0x34
+#define MT9V022_ANALOG_GAIN 0x35
#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47
#define MT9V022_PIXCLK_FV_LV 0x74
#define MT9V022_DIGITAL_TEST_PATTERN 0x7f
@@ -55,6 +55,13 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");
/* Progressive scan, master, defaults */
#define MT9V022_CHIP_CONTROL_DEFAULT 0x188
+#define MT9V022_MAX_WIDTH 752
+#define MT9V022_MAX_HEIGHT 480
+#define MT9V022_MIN_WIDTH 48
+#define MT9V022_MIN_HEIGHT 32
+#define MT9V022_COLUMN_SKIP 1
+#define MT9V022_ROW_SKIP 4
+
static const struct soc_camera_data_format mt9v022_colour_formats[] = {
/* Order important: first natively supported,
* second supported with a GPIO extender */
@@ -85,12 +92,18 @@ static const struct soc_camera_data_format mt9v022_monochrome_formats[] = {
};
struct mt9v022 {
- struct i2c_client *client;
- struct soc_camera_device icd;
+ struct v4l2_subdev subdev;
+ struct v4l2_rect rect; /* Sensor window */
+ __u32 fourcc;
int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
u16 chip_control;
};
+static struct mt9v022 *to_mt9v022(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct mt9v022, subdev);
+}
+
static int reg_read(struct i2c_client *client, const u8 reg)
{
s32 data = i2c_smbus_read_word_data(client, reg);
@@ -125,29 +138,11 @@ static int reg_clear(struct i2c_client *client, const u8 reg,
return reg_write(client, reg, ret & ~data);
}
-static int mt9v022_init(struct soc_camera_device *icd)
+static int mt9v022_init(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
int ret;
- if (icl->power) {
- ret = icl->power(&client->dev, 1);
- if (ret < 0) {
- dev_err(icd->vdev->parent,
- "Platform failed to power-on the camera.\n");
- return ret;
- }
- }
-
- /*
- * The camera could have been already on, we hard-reset it additionally,
- * if available. Soft reset is done in video_probe().
- */
- if (icl->reset)
- icl->reset(&client->dev);
-
/* Almost the default mode: master, parallel, simultaneous, and an
* undocumented bit 0x200, which is present in table 7, but not in 8,
* plus snapshot mode to disable scan for now */
@@ -161,6 +156,10 @@ static int mt9v022_init(struct soc_camera_device *icd)
/* AEC, AGC on */
ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3);
if (!ret)
+ ret = reg_write(client, MT9V022_ANALOG_GAIN, 16);
+ if (!ret)
+ ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, 480);
+ if (!ret)
ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480);
if (!ret)
/* default - auto */
@@ -171,37 +170,19 @@ static int mt9v022_init(struct soc_camera_device *icd)
return ret;
}
-static int mt9v022_release(struct soc_camera_device *icd)
+static int mt9v022_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
-
- if (icl->power)
- icl->power(&mt9v022->client->dev, 0);
-
- return 0;
-}
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
-static int mt9v022_start_capture(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- /* Switch to master "normal" mode */
- mt9v022->chip_control &= ~0x10;
- if (reg_write(client, MT9V022_CHIP_CONTROL,
- mt9v022->chip_control) < 0)
- return -EIO;
- return 0;
-}
+ if (enable)
+ /* Switch to master "normal" mode */
+ mt9v022->chip_control &= ~0x10;
+ else
+ /* Switch to snapshot mode */
+ mt9v022->chip_control |= 0x10;
-static int mt9v022_stop_capture(struct soc_camera_device *icd)
-{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- /* Switch to snapshot mode */
- mt9v022->chip_control |= 0x10;
- if (reg_write(client, MT9V022_CHIP_CONTROL,
- mt9v022->chip_control) < 0)
+ if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0)
return -EIO;
return 0;
}
@@ -209,9 +190,9 @@ static int mt9v022_stop_capture(struct soc_camera_device *icd)
static int mt9v022_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
int ret;
u16 pixclk = 0;
@@ -255,7 +236,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
if (ret < 0)
return ret;
- dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
+ dev_dbg(&client->dev, "Calculated pixclk 0x%x, chip control 0x%x\n",
pixclk, mt9v022->chip_control);
return 0;
@@ -263,8 +244,7 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
{
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned int width_flag;
if (icl->query_bus_param)
@@ -280,60 +260,121 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
width_flag;
}
-static int mt9v022_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct v4l2_rect rect = a->c;
+ struct soc_camera_device *icd = client->dev.platform_data;
int ret;
+ /* Bayer format - even size lengths */
+ if (mt9v022->fourcc == V4L2_PIX_FMT_SBGGR8 ||
+ mt9v022->fourcc == V4L2_PIX_FMT_SBGGR16) {
+ rect.width = ALIGN(rect.width, 2);
+ rect.height = ALIGN(rect.height, 2);
+ /* Let the user play with the starting pixel */
+ }
+
+ soc_camera_limit_side(&rect.left, &rect.width,
+ MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH);
+
+ soc_camera_limit_side(&rect.top, &rect.height,
+ MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT);
+
/* Like in example app. Contradicts the datasheet though */
ret = reg_read(client, MT9V022_AEC_AGC_ENABLE);
if (ret >= 0) {
if (ret & 1) /* Autoexposure */
ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH,
- rect->height + icd->y_skip_top + 43);
+ rect.height + icd->y_skip_top + 43);
else
ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
- rect->height + icd->y_skip_top + 43);
+ rect.height + icd->y_skip_top + 43);
}
/* Setup frame format: defaults apart from width and height */
if (!ret)
- ret = reg_write(client, MT9V022_COLUMN_START, rect->left);
+ ret = reg_write(client, MT9V022_COLUMN_START, rect.left);
if (!ret)
- ret = reg_write(client, MT9V022_ROW_START, rect->top);
+ ret = reg_write(client, MT9V022_ROW_START, rect.top);
if (!ret)
/* Default 94, Phytec driver says:
* "width + horizontal blank >= 660" */
ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING,
- rect->width > 660 - 43 ? 43 :
- 660 - rect->width);
+ rect.width > 660 - 43 ? 43 :
+ 660 - rect.width);
if (!ret)
ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45);
if (!ret)
- ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect->width);
+ ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width);
if (!ret)
ret = reg_write(client, MT9V022_WINDOW_HEIGHT,
- rect->height + icd->y_skip_top);
+ rect.height + icd->y_skip_top);
if (ret < 0)
return ret;
- dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height);
+ dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect.width, rect.height);
+
+ mt9v022->rect = rect;
+
+ return 0;
+}
+
+static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+
+ a->c = mt9v022->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
return 0;
}
-static int mt9v022_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ a->bounds.left = MT9V022_COLUMN_SKIP;
+ a->bounds.top = MT9V022_ROW_SKIP;
+ a->bounds.width = MT9V022_MAX_WIDTH;
+ a->bounds.height = MT9V022_MAX_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int mt9v022_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_rect rect = {
- .left = icd->x_current,
- .top = icd->y_current,
- .width = pix->width,
- .height = pix->height,
+
+ pix->width = mt9v022->rect.width;
+ pix->height = mt9v022->rect.height;
+ pix->pixelformat = mt9v022->fourcc;
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+
+ return 0;
+}
+
+static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_crop a = {
+ .c = {
+ .left = mt9v022->rect.left,
+ .top = mt9v022->rect.top,
+ .width = pix->width,
+ .height = pix->height,
+ },
};
+ int ret;
/* The caller provides a supported format, as verified per call to
* icd->try_fmt(), datawidth is from our supported format list */
@@ -356,30 +397,42 @@ static int mt9v022_set_fmt(struct soc_camera_device *icd,
}
/* No support for scaling on this camera, just crop. */
- return mt9v022_set_crop(icd, &rect);
+ ret = mt9v022_s_crop(sd, &a);
+ if (!ret) {
+ pix->width = mt9v022->rect.width;
+ pix->height = mt9v022->rect.height;
+ mt9v022->fourcc = pix->pixelformat;
+ }
+
+ return ret;
}
-static int mt9v022_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
+ struct i2c_client *client = sd->priv;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct v4l2_pix_format *pix = &f->fmt.pix;
+ int align = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
+ pix->pixelformat == V4L2_PIX_FMT_SBGGR16;
- v4l_bound_align_image(&pix->width, 48, 752, 2 /* ? */,
- &pix->height, 32 + icd->y_skip_top,
- 480 + icd->y_skip_top, 0, 0);
+ v4l_bound_align_image(&pix->width, MT9V022_MIN_WIDTH,
+ MT9V022_MAX_WIDTH, align,
+ &pix->height, MT9V022_MIN_HEIGHT + icd->y_skip_top,
+ MT9V022_MAX_HEIGHT + icd->y_skip_top, align, 0);
return 0;
}
-static int mt9v022_get_chip_id(struct soc_camera_device *icd,
- struct v4l2_dbg_chip_ident *id)
+static int mt9v022_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
{
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ struct i2c_client *client = sd->priv;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
return -EINVAL;
- if (id->match.addr != mt9v022->client->addr)
+ if (id->match.addr != client->addr)
return -ENODEV;
id->ident = mt9v022->model;
@@ -389,10 +442,10 @@ static int mt9v022_get_chip_id(struct soc_camera_device *icd,
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int mt9v022_get_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9v022_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
return -EINVAL;
@@ -409,10 +462,10 @@ static int mt9v022_get_register(struct soc_camera_device *icd,
return 0;
}
-static int mt9v022_set_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int mt9v022_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
return -EINVAL;
@@ -481,41 +534,22 @@ static const struct v4l2_queryctrl mt9v022_controls[] = {
}
};
-static int mt9v022_video_probe(struct soc_camera_device *);
-static void mt9v022_video_remove(struct soc_camera_device *);
-static int mt9v022_get_control(struct soc_camera_device *, struct v4l2_control *);
-static int mt9v022_set_control(struct soc_camera_device *, struct v4l2_control *);
-
static struct soc_camera_ops mt9v022_ops = {
- .owner = THIS_MODULE,
- .probe = mt9v022_video_probe,
- .remove = mt9v022_video_remove,
- .init = mt9v022_init,
- .release = mt9v022_release,
- .start_capture = mt9v022_start_capture,
- .stop_capture = mt9v022_stop_capture,
- .set_crop = mt9v022_set_crop,
- .set_fmt = mt9v022_set_fmt,
- .try_fmt = mt9v022_try_fmt,
.set_bus_param = mt9v022_set_bus_param,
.query_bus_param = mt9v022_query_bus_param,
.controls = mt9v022_controls,
.num_controls = ARRAY_SIZE(mt9v022_controls),
- .get_control = mt9v022_get_control,
- .set_control = mt9v022_set_control,
- .get_chip_id = mt9v022_get_chip_id,
-#ifdef CONFIG_VIDEO_ADV_DEBUG
- .get_register = mt9v022_get_register,
- .set_register = mt9v022_set_register,
-#endif
};
-static int mt9v022_get_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
+static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
+ const struct v4l2_queryctrl *qctrl;
+ unsigned long range;
int data;
+ qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
+
switch (ctrl->id) {
case V4L2_CID_VFLIP:
data = reg_read(client, MT9V022_READ_MODE);
@@ -541,19 +575,35 @@ static int mt9v022_get_control(struct soc_camera_device *icd,
return -EIO;
ctrl->value = !!(data & 0x2);
break;
+ case V4L2_CID_GAIN:
+ data = reg_read(client, MT9V022_ANALOG_GAIN);
+ if (data < 0)
+ return -EIO;
+
+ range = qctrl->maximum - qctrl->minimum;
+ ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum;
+
+ break;
+ case V4L2_CID_EXPOSURE:
+ data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH);
+ if (data < 0)
+ return -EIO;
+
+ range = qctrl->maximum - qctrl->minimum;
+ ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum;
+
+ break;
}
return 0;
}
-static int mt9v022_set_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
+static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
int data;
- struct i2c_client *client = to_i2c_client(icd->control);
+ struct i2c_client *client = sd->priv;
const struct v4l2_queryctrl *qctrl;
qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id);
-
if (!qctrl)
return -EINVAL;
@@ -580,12 +630,9 @@ static int mt9v022_set_control(struct soc_camera_device *icd,
return -EINVAL;
else {
unsigned long range = qctrl->maximum - qctrl->minimum;
- /* Datasheet says 16 to 64. autogain only works properly
- * after setting gain to maximum 14. Larger values
- * produce "white fly" noise effect. On the whole,
- * manually setting analog gain does no good. */
+ /* Valid values 16 to 64, 32 to 64 must be even. */
unsigned long gain = ((ctrl->value - qctrl->minimum) *
- 10 + range / 2) / range + 4;
+ 48 + range / 2) / range + 16;
if (gain >= 32)
gain &= ~1;
/* The user wants to set gain manually, hope, she
@@ -594,11 +641,10 @@ static int mt9v022_set_control(struct soc_camera_device *icd,
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0)
return -EIO;
- dev_info(&icd->dev, "Setting gain from %d to %lu\n",
- reg_read(client, MT9V022_ANALOG_GAIN), gain);
+ dev_dbg(&client->dev, "Setting gain from %d to %lu\n",
+ reg_read(client, MT9V022_ANALOG_GAIN), gain);
if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0)
return -EIO;
- icd->gain = ctrl->value;
}
break;
case V4L2_CID_EXPOSURE:
@@ -615,13 +661,12 @@ static int mt9v022_set_control(struct soc_camera_device *icd,
if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0)
return -EIO;
- dev_dbg(&icd->dev, "Shutter width from %d to %lu\n",
+ dev_dbg(&client->dev, "Shutter width from %d to %lu\n",
reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH),
shutter);
if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH,
shutter) < 0)
return -EIO;
- icd->exposure = ctrl->value;
}
break;
case V4L2_CID_AUTOGAIN:
@@ -646,11 +691,11 @@ static int mt9v022_set_control(struct soc_camera_device *icd,
/* Interface active, can use i2c. If it fails, it can indeed mean, that
* this wasn't our capture interface, so, we wait for the right one */
-static int mt9v022_video_probe(struct soc_camera_device *icd)
+static int mt9v022_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(icd->control);
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
s32 data;
int ret;
unsigned long flags;
@@ -665,7 +710,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
/* must be 0x1311 or 0x1313 */
if (data != 0x1311 && data != 0x1313) {
ret = -ENODEV;
- dev_info(&icd->dev, "No MT9V022 detected, ID register 0x%x\n",
+ dev_info(&client->dev, "No MT9V022 found, ID register 0x%x\n",
data);
goto ei2c;
}
@@ -677,7 +722,9 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
/* 15 clock cycles */
udelay(200);
if (reg_read(client, MT9V022_RESET)) {
- dev_err(&icd->dev, "Resetting MT9V022 failed!\n");
+ dev_err(&client->dev, "Resetting MT9V022 failed!\n");
+ if (ret > 0)
+ ret = -EIO;
goto ei2c;
}
@@ -694,7 +741,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
}
if (ret < 0)
- goto eisis;
+ goto ei2c;
icd->num_formats = 0;
@@ -716,33 +763,55 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
if (flags & SOCAM_DATAWIDTH_8)
icd->num_formats++;
- ret = soc_camera_video_start(icd);
- if (ret < 0)
- goto eisis;
+ mt9v022->fourcc = icd->formats->fourcc;
- dev_info(&icd->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
+ dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n",
data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ?
"monochrome" : "colour");
- return 0;
+ ret = mt9v022_init(client);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to initialise the camera\n");
-eisis:
ei2c:
return ret;
}
static void mt9v022_video_remove(struct soc_camera_device *icd)
{
- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
- struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
- dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr,
+ dev_dbg(&icd->dev, "Video removed: %p, %p\n",
icd->dev.parent, icd->vdev);
- soc_camera_video_stop(icd);
if (icl->free_bus)
icl->free_bus(icl);
}
+static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = {
+ .g_ctrl = mt9v022_g_ctrl,
+ .s_ctrl = mt9v022_s_ctrl,
+ .g_chip_ident = mt9v022_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9v022_g_register,
+ .s_register = mt9v022_s_register,
+#endif
+};
+
+static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = {
+ .s_stream = mt9v022_s_stream,
+ .s_fmt = mt9v022_s_fmt,
+ .g_fmt = mt9v022_g_fmt,
+ .try_fmt = mt9v022_try_fmt,
+ .s_crop = mt9v022_s_crop,
+ .g_crop = mt9v022_g_crop,
+ .cropcap = mt9v022_cropcap,
+};
+
+static struct v4l2_subdev_ops mt9v022_subdev_ops = {
+ .core = &mt9v022_subdev_core_ops,
+ .video = &mt9v022_subdev_video_ops,
+};
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
static int mt9v022_probe(struct i2c_client *client,
const struct i2c_device_id *did)
@@ -751,11 +820,17 @@ static int mt9v022_probe(struct i2c_client *client)
#endif
{
struct mt9v022 *mt9v022;
- struct soc_camera_device *icd;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct soc_camera_link *icl = client->dev.platform_data;
+ struct soc_camera_link *icl;
int ret;
+ if (!icd) {
+ dev_err(&client->dev, "MT9V022: missing soc-camera data!\n");
+ return -EINVAL;
+ }
+
+ icl = to_soc_camera_link(icd);
if (!icl) {
dev_err(&client->dev, "MT9V022 driver needs platform data\n");
return -EINVAL;
@@ -771,40 +846,41 @@ static int mt9v022_probe(struct i2c_client *client)
if (!mt9v022)
return -ENOMEM;
+ v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops);
+
mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT;
- mt9v022->client = client;
- i2c_set_clientdata(client, mt9v022);
-
- icd = &mt9v022->icd;
- icd->ops = &mt9v022_ops;
- icd->control = &client->dev;
- icd->x_min = 1;
- icd->y_min = 4;
- icd->x_current = 1;
- icd->y_current = 4;
- icd->width_min = 48;
- icd->width_max = 752;
- icd->height_min = 32;
- icd->height_max = 480;
- icd->y_skip_top = 1;
- icd->iface = icl->bus_id;
-
- ret = soc_camera_device_register(icd);
- if (ret)
- goto eisdr;
- return 0;
+ icd->ops = &mt9v022_ops;
+ /*
+ * MT9V022 _really_ corrupts the first read out line.
+ * TODO: verify on i.MX31
+ */
+ icd->y_skip_top = 1;
+
+ mt9v022->rect.left = MT9V022_COLUMN_SKIP;
+ mt9v022->rect.top = MT9V022_ROW_SKIP;
+ mt9v022->rect.width = MT9V022_MAX_WIDTH;
+ mt9v022->rect.height = MT9V022_MAX_HEIGHT;
+
+ ret = mt9v022_video_probe(icd, client);
+ if (ret) {
+ icd->ops = NULL;
+ i2c_set_clientdata(client, NULL);
+ kfree(mt9v022);
+ }
-eisdr:
- kfree(mt9v022);
return ret;
}
static int mt9v022_remove(struct i2c_client *client)
{
- struct mt9v022 *mt9v022 = i2c_get_clientdata(client);
+ struct mt9v022 *mt9v022 = to_mt9v022(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
- soc_camera_device_unregister(&mt9v022->icd);
+ icd->ops = NULL;
+ mt9v022_video_remove(icd);
+ i2c_set_clientdata(client, NULL);
+ client->driver = NULL;
kfree(mt9v022);
return 0;
diff --git a/linux/drivers/media/video/mx1_camera.c b/linux/drivers/media/video/mx1_camera.c
index 2d075205b..5f37952c7 100644
--- a/linux/drivers/media/video/mx1_camera.c
+++ b/linux/drivers/media/video/mx1_camera.c
@@ -126,7 +126,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
{
struct soc_camera_device *icd = vq->priv_data;
- *size = icd->width * icd->height *
+ *size = icd->user_width * icd->user_height *
((icd->current_fmt->depth + 7) >> 3);
if (!*count)
@@ -135,7 +135,7 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
while (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
(*count)--;
- dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
return 0;
}
@@ -147,7 +147,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf)
BUG_ON(in_interrupt());
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/* This waits until this buffer is out of danger, i.e., until it is no
@@ -165,7 +165,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
int ret;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/* Added list head initialization on alloc */
@@ -178,12 +178,12 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq,
buf->inwork = 1;
if (buf->fmt != icd->current_fmt ||
- vb->width != icd->width ||
- vb->height != icd->height ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height ||
vb->field != field) {
buf->fmt = icd->current_fmt;
- vb->width = icd->width;
- vb->height = icd->height;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
@@ -216,10 +216,11 @@ out:
static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
{
struct videobuf_buffer *vbuf = &pcdev->active->vb;
+ struct device *dev = pcdev->icd->dev.parent;
int ret;
if (unlikely(!pcdev->active)) {
- dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n");
+ dev_err(dev, "DMA End IRQ with no active buffer\n");
return -EFAULT;
}
@@ -229,11 +230,12 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev)
vbuf->size, pcdev->res->start +
CSIRXR, DMA_MODE_READ);
if (unlikely(ret))
- dev_err(pcdev->soc_host.dev, "Failed to setup DMA sg list\n");
+ dev_err(dev, "Failed to setup DMA sg list\n");
return ret;
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void mx1_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -241,13 +243,10 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx1_camera_dev *pcdev = ici->priv;
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
- unsigned long flags;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
- spin_lock_irqsave(&pcdev->lock, flags);
-
list_add_tail(&vb->queue, &pcdev->capture);
vb->state = VIDEOBUF_ACTIVE;
@@ -264,8 +263,6 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq,
__raw_writel(temp, pcdev->base + CSICR1);
}
}
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void mx1_videobuf_release(struct videobuf_queue *vq,
@@ -274,22 +271,23 @@ static void mx1_videobuf_release(struct videobuf_queue *vq,
struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb);
#ifdef DEBUG
struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->dev.parent;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
switch (vb->state) {
case VIDEOBUF_ACTIVE:
- dev_dbg(&icd->dev, "%s (active)\n", __func__);
+ dev_dbg(dev, "%s (active)\n", __func__);
break;
case VIDEOBUF_QUEUED:
- dev_dbg(&icd->dev, "%s (queued)\n", __func__);
+ dev_dbg(dev, "%s (queued)\n", __func__);
break;
case VIDEOBUF_PREPARED:
- dev_dbg(&icd->dev, "%s (prepared)\n", __func__);
+ dev_dbg(dev, "%s (prepared)\n", __func__);
break;
default:
- dev_dbg(&icd->dev, "%s (unknown)\n", __func__);
+ dev_dbg(dev, "%s (unknown)\n", __func__);
break;
}
#endif
@@ -329,6 +327,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev,
static void mx1_camera_dma_irq(int channel, void *data)
{
struct mx1_camera_dev *pcdev = data;
+ struct device *dev = pcdev->icd->dev.parent;
struct mx1_buffer *buf;
struct videobuf_buffer *vb;
unsigned long flags;
@@ -338,14 +337,14 @@ static void mx1_camera_dma_irq(int channel, void *data)
imx_dma_disable(channel);
if (unlikely(!pcdev->active)) {
- dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n");
+ dev_err(dev, "DMA End IRQ with no active buffer\n");
goto out;
}
vb = &pcdev->active->vb;
buf = container_of(vb, struct mx1_buffer, vb);
WARN_ON(buf->inwork || list_empty(&vb->queue));
- dev_dbg(pcdev->soc_host.dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
mx1_camera_wakeup(pcdev, vb, buf);
@@ -366,7 +365,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx1_camera_dev *pcdev = ici->priv;
- videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, ici->dev,
+ videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, icd->dev.parent,
&pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE,
@@ -385,8 +384,9 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev)
* they get a nice Oops */
div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1;
- dev_dbg(pcdev->soc_host.dev, "System clock %lukHz, target freq %dkHz, "
- "divisor %lu\n", lcdclk / 1000, mclk / 1000, div);
+ dev_dbg(pcdev->icd->dev.parent,
+ "System clock %lukHz, target freq %dkHz, divisor %lu\n",
+ lcdclk / 1000, mclk / 1000, div);
return div;
}
@@ -395,7 +395,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
{
unsigned int csicr1 = CSICR1_EN;
- dev_dbg(pcdev->soc_host.dev, "Activate device\n");
+ dev_dbg(pcdev->icd->dev.parent, "Activate device\n");
clk_enable(pcdev->clk);
@@ -411,7 +411,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev)
static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev)
{
- dev_dbg(pcdev->soc_host.dev, "Deactivate device\n");
+ dev_dbg(pcdev->icd->dev.parent, "Deactivate device\n");
/* Disable all CSI interface */
__raw_writel(0x00, pcdev->base + CSICR1);
@@ -432,14 +432,12 @@ static int mx1_camera_add_device(struct soc_camera_device *icd)
goto ebusy;
}
- dev_info(&icd->dev, "MX1 Camera driver attached to camera %d\n",
+ dev_info(icd->dev.parent, "MX1 Camera driver attached to camera %d\n",
icd->devnum);
mx1_camera_activate(pcdev);
- ret = icd->ops->init(icd);
- if (!ret)
- pcdev->icd = icd;
+ pcdev->icd = icd;
ebusy:
return ret;
@@ -460,20 +458,20 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd)
/* Stop DMA engine */
imx_dma_disable(pcdev->dma_chan);
- dev_info(&icd->dev, "MX1 Camera driver detached from camera %d\n",
+ dev_info(icd->dev.parent, "MX1 Camera driver detached from camera %d\n",
icd->devnum);
- icd->ops->release(icd);
-
mx1_camera_deactivate(pcdev);
pcdev->icd = NULL;
}
static int mx1_camera_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+ struct v4l2_crop *a)
{
- return icd->ops->set_crop(icd, rect);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+
+ return v4l2_subdev_call(sd, video, s_crop, a);
}
static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
@@ -543,18 +541,19 @@ static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
static int mx1_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat);
+ dev_warn(icd->dev.parent, "Format %x not found\n",
+ pix->pixelformat);
return -EINVAL;
}
- ret = icd->ops->set_fmt(icd, f);
+ ret = v4l2_subdev_call(sd, video, s_fmt, f);
if (!ret) {
icd->buswidth = xlate->buswidth;
icd->current_fmt = xlate->host_fmt;
@@ -566,10 +565,11 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd,
static int mx1_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
/* TODO: limit to mx1 hardware capabilities */
/* limit to sensor capabilities */
- return icd->ops->try_fmt(icd, f);
+ return v4l2_subdev_call(sd, video, try_fmt, f);
}
static int mx1_camera_reqbufs(struct soc_camera_file *icf,
@@ -741,7 +741,7 @@ static int __init mx1_camera_probe(struct platform_device *pdev)
pcdev->soc_host.drv_name = DRIVER_NAME;
pcdev->soc_host.ops = &mx1_soc_camera_host_ops;
pcdev->soc_host.priv = pcdev;
- pcdev->soc_host.dev = &pdev->dev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
err = soc_camera_host_register(&pcdev->soc_host);
if (err)
diff --git a/linux/drivers/media/video/mx3_camera.c b/linux/drivers/media/video/mx3_camera.c
index e605c076e..dff2e5e2d 100644
--- a/linux/drivers/media/video/mx3_camera.c
+++ b/linux/drivers/media/video/mx3_camera.c
@@ -178,7 +178,7 @@ static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf
BUG_ON(in_interrupt());
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/*
@@ -220,7 +220,7 @@ static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
if (!mx3_cam->idmac_channel[0])
return -EINVAL;
- *size = icd->width * icd->height * bpp;
+ *size = icd->user_width * icd->user_height * bpp;
if (!*count)
*count = 32;
@@ -241,7 +241,7 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq,
struct mx3_camera_buffer *buf =
container_of(vb, struct mx3_camera_buffer, vb);
/* current_fmt _must_ always be set */
- size_t new_size = icd->width * icd->height *
+ size_t new_size = icd->user_width * icd->user_height *
((icd->current_fmt->depth + 7) >> 3);
int ret;
@@ -251,12 +251,12 @@ static int mx3_videobuf_prepare(struct videobuf_queue *vq,
*/
if (buf->fmt != icd->current_fmt ||
- vb->width != icd->width ||
- vb->height != icd->height ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height ||
vb->field != field) {
buf->fmt = icd->current_fmt;
- vb->width = icd->width;
- vb->height = icd->height;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
vb->field = field;
if (vb->state != VIDEOBUF_NEEDS_INIT)
free_buffer(vq, buf);
@@ -332,7 +332,10 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
}
}
-/* Called with .vb_lock held */
+/*
+ * Called with .vb_lock mutex held and
+ * under spinlock_irqsave(&mx3_cam->lock, ...)
+ */
static void mx3_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -346,21 +349,20 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
struct idmac_video_param *video = &ichan->params.video;
const struct soc_camera_data_format *data_fmt = icd->current_fmt;
dma_cookie_t cookie;
- unsigned long flags;
+
+ BUG_ON(!irqs_disabled());
/* This is the configuration of one sg-element */
video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc);
- video->out_width = icd->width;
- video->out_height = icd->height;
- video->out_stride = icd->width;
+ video->out_width = icd->user_width;
+ video->out_height = icd->user_height;
+ video->out_stride = icd->user_width;
#ifdef DEBUG
/* helps to see what DMA actually has written */
memset((void *)vb->baddr, 0xaa, vb->bsize);
#endif
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
list_add_tail(&vb->queue, &mx3_cam->capture);
if (!mx3_cam->active) {
@@ -370,24 +372,24 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq,
vb->state = VIDEOBUF_QUEUED;
}
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
+ spin_unlock_irq(&mx3_cam->lock);
cookie = txd->tx_submit(txd);
- dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg));
+ dev_dbg(icd->dev.parent, "Submitted cookie %d DMA 0x%08x\n",
+ cookie, sg_dma_address(&buf->sg));
+
+ spin_lock_irq(&mx3_cam->lock);
+
if (cookie >= 0)
return;
/* Submit error */
vb->state = VIDEOBUF_PREPARED;
- spin_lock_irqsave(&mx3_cam->lock, flags);
-
list_del_init(&vb->queue);
if (mx3_cam->active == buf)
mx3_cam->active = NULL;
-
- spin_unlock_irqrestore(&mx3_cam->lock, flags);
}
/* Called with .vb_lock held */
@@ -401,9 +403,10 @@ static void mx3_videobuf_release(struct videobuf_queue *vq,
container_of(vb, struct mx3_camera_buffer, vb);
unsigned long flags;
- dev_dbg(&icd->dev, "Release%s DMA 0x%08x (state %d), queue %sempty\n",
+ dev_dbg(icd->dev.parent,
+ "Release%s DMA 0x%08x (state %d), queue %sempty\n",
mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
- vb->state, list_empty(&vb->queue) ? "" : "not ");
+ vb->state, list_empty(&vb->queue) ? "" : "not ");
spin_lock_irqsave(&mx3_cam->lock, flags);
if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
!list_empty(&vb->queue)) {
@@ -430,7 +433,7 @@ static void mx3_camera_init_videobuf(struct videobuf_queue *q,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, ici->dev,
+ videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, icd->dev.parent,
&mx3_cam->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_NONE,
@@ -483,7 +486,7 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
clk_enable(mx3_cam->clk);
rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
- dev_dbg(&icd->dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
+ dev_dbg(icd->dev.parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
if (rate)
clk_set_rate(mx3_cam->clk, rate);
}
@@ -493,29 +496,18 @@ static int mx3_camera_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
- int ret;
- if (mx3_cam->icd) {
- ret = -EBUSY;
- goto ebusy;
- }
+ if (mx3_cam->icd)
+ return -EBUSY;
mx3_camera_activate(mx3_cam, icd);
- ret = icd->ops->init(icd);
- if (ret < 0) {
- clk_disable(mx3_cam->clk);
- goto einit;
- }
mx3_cam->icd = icd;
-einit:
-ebusy:
- if (!ret)
- dev_info(&icd->dev, "MX3 Camera driver attached to camera %d\n",
- icd->devnum);
+ dev_info(icd->dev.parent, "MX3 Camera driver attached to camera %d\n",
+ icd->devnum);
- return ret;
+ return 0;
}
/* Called with .video_lock held */
@@ -532,13 +524,11 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd)
*ichan = NULL;
}
- icd->ops->release(icd);
-
clk_disable(mx3_cam->clk);
mx3_cam->icd = NULL;
- dev_info(&icd->dev, "MX3 Camera driver detached from camera %d\n",
+ dev_info(icd->dev.parent, "MX3 Camera driver detached from camera %d\n",
icd->devnum);
}
@@ -550,7 +540,8 @@ static bool channel_change_requested(struct soc_camera_device *icd,
struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
/* Do buffers have to be re-allocated or channel re-configured? */
- return ichan && rect->width * rect->height > icd->width * icd->height;
+ return ichan && rect->width * rect->height >
+ icd->user_width * icd->user_height;
}
static int test_platform_param(struct mx3_camera_dev *mx3_cam,
@@ -598,8 +589,8 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
*flags |= SOCAM_DATAWIDTH_4;
break;
default:
- dev_info(mx3_cam->soc_host.dev, "Unsupported bus width %d\n",
- buswidth);
+ dev_warn(mx3_cam->soc_host.v4l2_dev.dev,
+ "Unsupported bus width %d\n", buswidth);
return -EINVAL;
}
@@ -614,7 +605,7 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
unsigned long bus_flags, camera_flags;
int ret = test_platform_param(mx3_cam, depth, &bus_flags);
- dev_dbg(ici->dev, "requested bus width %d bit: %d\n", depth, ret);
+ dev_dbg(icd->dev.parent, "request bus width %d bit: %d\n", depth, ret);
if (ret < 0)
return ret;
@@ -623,7 +614,8 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
ret = soc_camera_bus_param_compatible(camera_flags, bus_flags);
if (ret < 0)
- dev_warn(&icd->dev, "Flags incompatible: camera %lx, host %lx\n",
+ dev_warn(icd->dev.parent,
+ "Flags incompatible: camera %lx, host %lx\n",
camera_flags, bus_flags);
return ret;
@@ -637,7 +629,7 @@ static bool chan_filter(struct dma_chan *chan, void *arg)
if (!rq)
return false;
- pdata = rq->mx3_cam->soc_host.dev->platform_data;
+ pdata = rq->mx3_cam->soc_host.v4l2_dev.dev->platform_data;
return rq->id == chan->chan_id &&
pdata->dma_dev == chan->device->dev;
@@ -697,7 +689,8 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx,
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = buswidth;
xlate++;
- dev_dbg(ici->dev, "Providing format %s using %s\n",
+ dev_dbg(icd->dev.parent,
+ "Providing format %s using %s\n",
mx3_camera_formats[0].name,
icd->formats[idx].name);
}
@@ -709,7 +702,8 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx,
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = buswidth;
xlate++;
- dev_dbg(ici->dev, "Providing format %s using %s\n",
+ dev_dbg(icd->dev.parent,
+ "Providing format %s using %s\n",
mx3_camera_formats[0].name,
icd->formats[idx].name);
}
@@ -722,7 +716,7 @@ passthrough:
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = buswidth;
xlate++;
- dev_dbg(ici->dev,
+ dev_dbg(icd->dev.parent,
"Providing format %s in pass-through mode\n",
icd->formats[idx].name);
}
@@ -732,13 +726,13 @@ passthrough:
}
static void configure_geometry(struct mx3_camera_dev *mx3_cam,
- struct v4l2_rect *rect)
+ unsigned int width, unsigned int height)
{
u32 ctrl, width_field, height_field;
/* Setup frame size - this cannot be changed on-the-fly... */
- width_field = rect->width - 1;
- height_field = rect->height - 1;
+ width_field = width - 1;
+ height_field = height - 1;
csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
@@ -750,11 +744,6 @@ static void configure_geometry(struct mx3_camera_dev *mx3_cam,
ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
/* Sensor does the cropping */
csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
-
- /*
- * No need to free resources here if we fail, we'll see if we need to
- * do this next time we are called
- */
}
static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
@@ -791,25 +780,74 @@ static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
return 0;
}
+/*
+ * FIXME: learn to use stride != width, then we can keep stride properly aligned
+ * and support arbitrary (even) widths.
+ */
+static inline void stride_align(__s32 *width)
+{
+ if (((*width + 7) & ~7) < 4096)
+ *width = (*width + 7) & ~7;
+ else
+ *width = *width & ~7;
+}
+
+/*
+ * As long as we don't implement host-side cropping and scaling, we can use
+ * default g_crop and cropcap from soc_camera.c
+ */
static int mx3_camera_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+ struct v4l2_crop *a)
{
+ struct v4l2_rect *rect = &a->c;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
+ struct v4l2_pix_format *pix = &f.fmt.pix;
+ int ret;
- /*
- * We now know pixel formats and can decide upon DMA-channel(s)
- * So far only direct camera-to-memory is supported
- */
- if (channel_change_requested(icd, rect)) {
- int ret = acquire_dma_channel(mx3_cam);
+ soc_camera_limit_side(&rect->left, &rect->width, 0, 2, 4096);
+ soc_camera_limit_side(&rect->top, &rect->height, 0, 2, 4096);
+
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
+ if (ret < 0)
+ return ret;
+
+ /* The capture device might have changed its output */
+ ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ if (ret < 0)
+ return ret;
+
+ if (pix->width & 7) {
+ /* Ouch! We can only handle 8-byte aligned width... */
+ stride_align(&pix->width);
+ ret = v4l2_subdev_call(sd, video, s_fmt, &f);
if (ret < 0)
return ret;
}
- configure_geometry(mx3_cam, rect);
+ if (pix->width != icd->user_width || pix->height != icd->user_height) {
+ /*
+ * We now know pixel formats and can decide upon DMA-channel(s)
+ * So far only direct camera-to-memory is supported
+ */
+ if (channel_change_requested(icd, rect)) {
+ int ret = acquire_dma_channel(mx3_cam);
+ if (ret < 0)
+ return ret;
+ }
+
+ configure_geometry(mx3_cam, pix->width, pix->height);
+ }
+
+ dev_dbg(icd->dev.parent, "Sensor cropped %dx%d\n",
+ pix->width, pix->height);
+
+ icd->user_width = pix->width;
+ icd->user_height = pix->height;
- return icd->ops->set_crop(icd, rect);
+ return ret;
}
static int mx3_camera_set_fmt(struct soc_camera_device *icd,
@@ -817,22 +855,21 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_rect rect = {
- .left = icd->x_current,
- .top = icd->y_current,
- .width = pix->width,
- .height = pix->height,
- };
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat);
+ dev_warn(icd->dev.parent, "Format %x not found\n",
+ pix->pixelformat);
return -EINVAL;
}
+ stride_align(&pix->width);
+ dev_dbg(icd->dev.parent, "Set format %dx%d\n", pix->width, pix->height);
+
ret = acquire_dma_channel(mx3_cam);
if (ret < 0)
return ret;
@@ -843,21 +880,23 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
* mxc_v4l2_s_fmt()
*/
- configure_geometry(mx3_cam, &rect);
+ configure_geometry(mx3_cam, pix->width, pix->height);
- ret = icd->ops->set_fmt(icd, f);
+ ret = v4l2_subdev_call(sd, video, s_fmt, f);
if (!ret) {
icd->buswidth = xlate->buswidth;
icd->current_fmt = xlate->host_fmt;
}
+ dev_dbg(icd->dev.parent, "Sensor set %dx%d\n", pix->width, pix->height);
+
return ret;
}
static int mx3_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
__u32 pixfmt = pix->pixelformat;
@@ -866,7 +905,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (pixfmt && !xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pixfmt);
+ dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
@@ -883,7 +922,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
/* camera has to see its format, but the user the original one */
pix->pixelformat = xlate->cam_fmt->fourcc;
/* limit to sensor capabilities */
- ret = icd->ops->try_fmt(icd, f);
+ ret = v4l2_subdev_call(sd, video, try_fmt, f);
pix->pixelformat = xlate->host_fmt->fourcc;
field = pix->field;
@@ -891,7 +930,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd,
if (field == V4L2_FIELD_ANY) {
pix->field = V4L2_FIELD_NONE;
} else if (field != V4L2_FIELD_NONE) {
- dev_err(&icd->dev, "Field type %d unsupported.\n", field);
+ dev_err(icd->dev.parent, "Field type %d unsupported.\n", field);
return -EINVAL;
}
@@ -930,14 +969,15 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
u32 dw, sens_conf;
int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags);
const struct soc_camera_format_xlate *xlate;
+ struct device *dev = icd->dev.parent;
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pixfmt);
+ dev_warn(dev, "Format %x not found\n", pixfmt);
return -EINVAL;
}
- dev_dbg(ici->dev, "requested bus width %d bit: %d\n",
+ dev_dbg(dev, "requested bus width %d bit: %d\n",
icd->buswidth, ret);
if (ret < 0)
@@ -946,9 +986,10 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
camera_flags = icd->ops->query_bus_param(icd);
common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
+ dev_dbg(dev, "Flags cam: 0x%lx host: 0x%lx common: 0x%lx\n",
+ camera_flags, bus_flags, common_flags);
if (!common_flags) {
- dev_dbg(ici->dev, "no common flags: camera %lx, host %lx\n",
- camera_flags, bus_flags);
+ dev_dbg(dev, "no common flags");
return -EINVAL;
}
@@ -1001,8 +1042,11 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
SOCAM_DATAWIDTH_4;
ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
+ if (ret < 0) {
+ dev_dbg(dev, "camera set_bus_param(%lx) returned %d\n",
+ common_flags, ret);
return ret;
+ }
/*
* So far only gated clock mode is supported. Add a line
@@ -1054,7 +1098,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
- dev_dbg(ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw);
+ dev_dbg(dev, "Set SENS_CONF to %x\n", sens_conf | dw);
return 0;
}
@@ -1126,8 +1170,9 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mx3_cam->capture);
spin_lock_init(&mx3_cam->lock);
- base = ioremap(res->start, res->end - res->start + 1);
+ base = ioremap(res->start, resource_size(res));
if (!base) {
+ pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
err = -ENOMEM;
goto eioremap;
}
@@ -1138,7 +1183,7 @@ static int __devinit mx3_camera_probe(struct platform_device *pdev)
soc_host->drv_name = MX3_CAM_DRV_NAME;
soc_host->ops = &mx3_soc_camera_host_ops;
soc_host->priv = mx3_cam;
- soc_host->dev = &pdev->dev;
+ soc_host->v4l2_dev.dev = &pdev->dev;
soc_host->nr = pdev->id;
err = soc_camera_host_register(soc_host);
@@ -1214,3 +1259,4 @@ module_exit(mx3_camera_exit);
MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" MX3_CAM_DRV_NAME);
diff --git a/linux/drivers/media/video/ov772x.c b/linux/drivers/media/video/ov772x.c
index 4b0363940..47e7c7c53 100644
--- a/linux/drivers/media/video/ov772x.c
+++ b/linux/drivers/media/video/ov772x.c
@@ -22,7 +22,7 @@
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
#include <media/soc_camera.h>
#include <media/ov772x.h>
@@ -382,11 +382,10 @@ struct regval_list {
};
struct ov772x_color_format {
- char *name;
- __u32 fourcc;
- u8 dsp3;
- u8 com3;
- u8 com7;
+ const struct soc_camera_data_format *format;
+ u8 dsp3;
+ u8 com3;
+ u8 com7;
};
struct ov772x_win_size {
@@ -398,14 +397,15 @@ struct ov772x_win_size {
};
struct ov772x_priv {
+ struct v4l2_subdev subdev;
struct ov772x_camera_info *info;
- struct i2c_client *client;
- struct soc_camera_device icd;
const struct ov772x_color_format *fmt;
const struct ov772x_win_size *win;
int model;
- unsigned int flag_vflip:1;
- unsigned int flag_hflip:1;
+ unsigned short flag_vflip:1;
+ unsigned short flag_hflip:1;
+ /* band_filter = COM8[5] ? 256 - BDBASE : 0 */
+ unsigned short band_filter;
};
#define ENDMARKER { 0xff, 0xff }
@@ -481,43 +481,43 @@ static const struct soc_camera_data_format ov772x_fmt_lists[] = {
*/
static const struct ov772x_color_format ov772x_cfmts[] = {
{
- SETFOURCC(YUYV),
+ .format = &ov772x_fmt_lists[0],
.dsp3 = 0x0,
.com3 = SWAP_YUV,
.com7 = OFMT_YUV,
},
{
- SETFOURCC(YVYU),
+ .format = &ov772x_fmt_lists[1],
.dsp3 = UV_ON,
.com3 = SWAP_YUV,
.com7 = OFMT_YUV,
},
{
- SETFOURCC(UYVY),
+ .format = &ov772x_fmt_lists[2],
.dsp3 = 0x0,
.com3 = 0x0,
.com7 = OFMT_YUV,
},
{
- SETFOURCC(RGB555),
+ .format = &ov772x_fmt_lists[3],
.dsp3 = 0x0,
.com3 = SWAP_RGB,
.com7 = FMT_RGB555 | OFMT_RGB,
},
{
- SETFOURCC(RGB555X),
+ .format = &ov772x_fmt_lists[4],
.dsp3 = 0x0,
.com3 = 0x0,
.com7 = FMT_RGB555 | OFMT_RGB,
},
{
- SETFOURCC(RGB565),
+ .format = &ov772x_fmt_lists[5],
.dsp3 = 0x0,
.com3 = SWAP_RGB,
.com7 = FMT_RGB565 | OFMT_RGB,
},
{
- SETFOURCC(RGB565X),
+ .format = &ov772x_fmt_lists[6],
.dsp3 = 0x0,
.com3 = 0x0,
.com7 = FMT_RGB565 | OFMT_RGB,
@@ -570,6 +570,15 @@ static const struct v4l2_queryctrl ov772x_controls[] = {
.step = 1,
.default_value = 0,
},
+ {
+ .id = V4L2_CID_BAND_STOP_FILTER,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Band-stop filter",
+ .minimum = 0,
+ .maximum = 256,
+ .step = 1,
+ .default_value = 0,
+ },
};
@@ -577,6 +586,12 @@ static const struct v4l2_queryctrl ov772x_controls[] = {
* general function
*/
+static struct ov772x_priv *to_ov772x(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct ov772x_priv,
+ subdev);
+}
+
static int ov772x_write_array(struct i2c_client *client,
const struct regval_list *vals)
{
@@ -617,58 +632,29 @@ static int ov772x_reset(struct i2c_client *client)
* soc_camera_ops function
*/
-static int ov772x_init(struct soc_camera_device *icd)
+static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
- int ret = 0;
+ struct i2c_client *client = sd->priv;
+ struct ov772x_priv *priv = to_ov772x(client);
- if (priv->info->link.power) {
- ret = priv->info->link.power(&priv->client->dev, 1);
- if (ret < 0)
- return ret;
+ if (!enable) {
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+ return 0;
}
- if (priv->info->link.reset)
- ret = priv->info->link.reset(&priv->client->dev);
-
- return ret;
-}
-
-static int ov772x_release(struct soc_camera_device *icd)
-{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
- int ret = 0;
-
- if (priv->info->link.power)
- ret = priv->info->link.power(&priv->client->dev, 0);
-
- return ret;
-}
-
-static int ov772x_start_capture(struct soc_camera_device *icd)
-{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
-
if (!priv->win || !priv->fmt) {
- dev_err(&icd->dev, "norm or win select error\n");
+ dev_err(&client->dev, "norm or win select error\n");
return -EPERM;
}
- ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, 0);
+ ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, 0);
- dev_dbg(&icd->dev,
- "format %s, win %s\n", priv->fmt->name, priv->win->name);
+ dev_dbg(&client->dev, "format %s, win %s\n",
+ priv->fmt->format->name, priv->win->name);
return 0;
}
-static int ov772x_stop_capture(struct soc_camera_device *icd)
-{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
- ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
- return 0;
-}
-
static int ov772x_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
@@ -677,8 +663,9 @@ static int ov772x_set_bus_param(struct soc_camera_device *icd,
static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
- struct soc_camera_link *icl = &priv->info->link;
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct ov772x_priv *priv = i2c_get_clientdata(client);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
@@ -686,10 +673,10 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
return soc_camera_apply_sensor_flags(icl, flags);
}
-static int ov772x_get_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
+static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct i2c_client *client = sd->priv;
+ struct ov772x_priv *priv = to_ov772x(client);
switch (ctrl->id) {
case V4L2_CID_VFLIP:
@@ -698,14 +685,17 @@ static int ov772x_get_control(struct soc_camera_device *icd,
case V4L2_CID_HFLIP:
ctrl->value = priv->flag_hflip;
break;
+ case V4L2_CID_BAND_STOP_FILTER:
+ ctrl->value = priv->band_filter;
+ break;
}
return 0;
}
-static int ov772x_set_control(struct soc_camera_device *icd,
- struct v4l2_control *ctrl)
+static int ov772x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct i2c_client *client = sd->priv;
+ struct ov772x_priv *priv = to_ov772x(client);
int ret = 0;
u8 val;
@@ -715,24 +705,48 @@ static int ov772x_set_control(struct soc_camera_device *icd,
priv->flag_vflip = ctrl->value;
if (priv->info->flags & OV772X_FLAG_VFLIP)
val ^= VFLIP_IMG;
- ret = ov772x_mask_set(priv->client, COM3, VFLIP_IMG, val);
+ ret = ov772x_mask_set(client, COM3, VFLIP_IMG, val);
break;
case V4L2_CID_HFLIP:
val = ctrl->value ? HFLIP_IMG : 0x00;
priv->flag_hflip = ctrl->value;
if (priv->info->flags & OV772X_FLAG_HFLIP)
val ^= HFLIP_IMG;
- ret = ov772x_mask_set(priv->client, COM3, HFLIP_IMG, val);
+ ret = ov772x_mask_set(client, COM3, HFLIP_IMG, val);
+ break;
+ case V4L2_CID_BAND_STOP_FILTER:
+ if ((unsigned)ctrl->value > 256)
+ ctrl->value = 256;
+ if (ctrl->value == priv->band_filter)
+ break;
+ if (!ctrl->value) {
+ /* Switch the filter off, it is on now */
+ ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff);
+ if (!ret)
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, 0);
+ } else {
+ /* Switch the filter on, set AEC low limit */
+ val = 256 - ctrl->value;
+ ret = ov772x_mask_set(client, COM8,
+ BNDF_ON_OFF, BNDF_ON_OFF);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, val);
+ }
+ if (!ret)
+ priv->band_filter = ctrl->value;
break;
}
return ret;
}
-static int ov772x_get_chip_id(struct soc_camera_device *icd,
- struct v4l2_dbg_chip_ident *id)
+static int ov772x_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct i2c_client *client = sd->priv;
+ struct ov772x_priv *priv = to_ov772x(client);
id->ident = priv->model;
id->revision = 0;
@@ -741,17 +755,17 @@ static int ov772x_get_chip_id(struct soc_camera_device *icd,
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int ov772x_get_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int ov772x_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
- int ret;
+ struct i2c_client *client = sd->priv;
+ int ret;
reg->size = 1;
if (reg->reg > 0xff)
return -EINVAL;
- ret = i2c_smbus_read_byte_data(priv->client, reg->reg);
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
if (ret < 0)
return ret;
@@ -760,21 +774,20 @@ static int ov772x_get_register(struct soc_camera_device *icd,
return 0;
}
-static int ov772x_set_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int ov772x_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct i2c_client *client = sd->priv;
if (reg->reg > 0xff ||
reg->val > 0xff)
return -EINVAL;
- return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val);
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
}
#endif
-static const struct ov772x_win_size*
-ov772x_select_win(u32 width, u32 height)
+static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height)
{
__u32 diff;
const struct ov772x_win_size *win;
@@ -793,9 +806,10 @@ ov772x_select_win(u32 width, u32 height)
return win;
}
-static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
- u32 pixfmt)
+static int ov772x_set_params(struct i2c_client *client,
+ u32 *width, u32 *height, u32 pixfmt)
{
+ struct ov772x_priv *priv = to_ov772x(client);
int ret = -EINVAL;
u8 val;
int i;
@@ -805,7 +819,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
*/
priv->fmt = NULL;
for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
- if (pixfmt == ov772x_cfmts[i].fourcc) {
+ if (pixfmt == ov772x_cfmts[i].format->fourcc) {
priv->fmt = ov772x_cfmts + i;
break;
}
@@ -816,12 +830,12 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
/*
* select win
*/
- priv->win = ov772x_select_win(width, height);
+ priv->win = ov772x_select_win(*width, *height);
/*
* reset hardware
*/
- ov772x_reset(priv->client);
+ ov772x_reset(client);
/*
* Edge Ctrl
@@ -835,17 +849,17 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
* Remove it when manual mode.
*/
- ret = ov772x_mask_set(priv->client, DSPAUTO, EDGE_ACTRL, 0x00);
+ ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00);
if (ret < 0)
goto ov772x_set_fmt_error;
- ret = ov772x_mask_set(priv->client,
+ ret = ov772x_mask_set(client,
EDGE_TRSHLD, EDGE_THRESHOLD_MASK,
priv->info->edgectrl.threshold);
if (ret < 0)
goto ov772x_set_fmt_error;
- ret = ov772x_mask_set(priv->client,
+ ret = ov772x_mask_set(client,
EDGE_STRNGT, EDGE_STRENGTH_MASK,
priv->info->edgectrl.strength);
if (ret < 0)
@@ -857,13 +871,13 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
*
* set upper and lower limit
*/
- ret = ov772x_mask_set(priv->client,
+ ret = ov772x_mask_set(client,
EDGE_UPPER, EDGE_UPPER_MASK,
priv->info->edgectrl.upper);
if (ret < 0)
goto ov772x_set_fmt_error;
- ret = ov772x_mask_set(priv->client,
+ ret = ov772x_mask_set(client,
EDGE_LOWER, EDGE_LOWER_MASK,
priv->info->edgectrl.lower);
if (ret < 0)
@@ -873,7 +887,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
/*
* set size format
*/
- ret = ov772x_write_array(priv->client, priv->win->regs);
+ ret = ov772x_write_array(client, priv->win->regs);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -882,7 +896,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
*/
val = priv->fmt->dsp3;
if (val) {
- ret = ov772x_mask_set(priv->client,
+ ret = ov772x_mask_set(client,
DSP_CTRL3, UV_MASK, val);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -901,7 +915,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
if (priv->flag_hflip)
val ^= HFLIP_IMG;
- ret = ov772x_mask_set(priv->client,
+ ret = ov772x_mask_set(client,
COM3, SWAP_MASK | IMG_MASK, val);
if (ret < 0)
goto ov772x_set_fmt_error;
@@ -910,47 +924,99 @@ static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
* set COM7
*/
val = priv->win->com7_bit | priv->fmt->com7;
- ret = ov772x_mask_set(priv->client,
+ ret = ov772x_mask_set(client,
COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK),
val);
if (ret < 0)
goto ov772x_set_fmt_error;
+ /*
+ * set COM8
+ */
+ if (priv->band_filter) {
+ ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, 1);
+ if (!ret)
+ ret = ov772x_mask_set(client, BDBASE,
+ 0xff, 256 - priv->band_filter);
+ if (ret < 0)
+ goto ov772x_set_fmt_error;
+ }
+
+ *width = priv->win->width;
+ *height = priv->win->height;
+
return ret;
ov772x_set_fmt_error:
- ov772x_reset(priv->client);
+ ov772x_reset(client);
priv->win = NULL;
priv->fmt = NULL;
return ret;
}
-static int ov772x_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+static int ov772x_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = VGA_WIDTH;
+ a->c.height = VGA_HEIGHT;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (!priv->fmt)
- return -EINVAL;
+ return 0;
+}
- return ov772x_set_params(priv, rect->width, rect->height,
- priv->fmt->fourcc);
+static int ov772x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = VGA_WIDTH;
+ a->bounds.height = VGA_HEIGHT;
+ a->defrect = a->bounds;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
}
-static int ov772x_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int ov772x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct ov772x_priv *priv = to_ov772x(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (!priv->win || !priv->fmt) {
+ u32 width = VGA_WIDTH, height = VGA_HEIGHT;
+ int ret = ov772x_set_params(client, &width, &height,
+ V4L2_PIX_FMT_YUYV);
+ if (ret < 0)
+ return ret;
+ }
+
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ pix->width = priv->win->width;
+ pix->height = priv->win->height;
+ pix->pixelformat = priv->fmt->format->fourcc;
+ pix->colorspace = priv->fmt->format->colorspace;
+ pix->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int ov772x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct i2c_client *client = sd->priv;
struct v4l2_pix_format *pix = &f->fmt.pix;
- return ov772x_set_params(priv, pix->width, pix->height,
+ return ov772x_set_params(client, &pix->width, &pix->height,
pix->pixelformat);
}
-static int ov772x_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int ov772x_try_fmt(struct v4l2_subdev *sd,
+ struct v4l2_format *f)
{
struct v4l2_pix_format *pix = &f->fmt.pix;
const struct ov772x_win_size *win;
@@ -967,9 +1033,10 @@ static int ov772x_try_fmt(struct soc_camera_device *icd,
return 0;
}
-static int ov772x_video_probe(struct soc_camera_device *icd)
+static int ov772x_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
{
- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ struct ov772x_priv *priv = to_ov772x(client);
u8 pid, ver;
const char *devname;
@@ -986,7 +1053,7 @@ static int ov772x_video_probe(struct soc_camera_device *icd)
*/
if (SOCAM_DATAWIDTH_10 != priv->info->buswidth &&
SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
- dev_err(&icd->dev, "bus width error\n");
+ dev_err(&client->dev, "bus width error\n");
return -ENODEV;
}
@@ -996,8 +1063,8 @@ static int ov772x_video_probe(struct soc_camera_device *icd)
/*
* check and show product ID and manufacturer ID
*/
- pid = i2c_smbus_read_byte_data(priv->client, PID);
- ver = i2c_smbus_read_byte_data(priv->client, VER);
+ pid = i2c_smbus_read_byte_data(client, PID);
+ ver = i2c_smbus_read_byte_data(client, VER);
switch (VERSION(pid, ver)) {
case OV7720:
@@ -1009,51 +1076,53 @@ static int ov772x_video_probe(struct soc_camera_device *icd)
priv->model = V4L2_IDENT_OV7725;
break;
default:
- dev_err(&icd->dev,
+ dev_err(&client->dev,
"Product ID error %x:%x\n", pid, ver);
return -ENODEV;
}
- dev_info(&icd->dev,
+ dev_info(&client->dev,
"%s Product ID %0x:%0x Manufacturer ID %x:%x\n",
devname,
pid,
ver,
- i2c_smbus_read_byte_data(priv->client, MIDH),
- i2c_smbus_read_byte_data(priv->client, MIDL));
-
- return soc_camera_video_start(icd);
-}
+ i2c_smbus_read_byte_data(client, MIDH),
+ i2c_smbus_read_byte_data(client, MIDL));
-static void ov772x_video_remove(struct soc_camera_device *icd)
-{
- soc_camera_video_stop(icd);
+ return 0;
}
static struct soc_camera_ops ov772x_ops = {
- .owner = THIS_MODULE,
- .probe = ov772x_video_probe,
- .remove = ov772x_video_remove,
- .init = ov772x_init,
- .release = ov772x_release,
- .start_capture = ov772x_start_capture,
- .stop_capture = ov772x_stop_capture,
- .set_crop = ov772x_set_crop,
- .set_fmt = ov772x_set_fmt,
- .try_fmt = ov772x_try_fmt,
.set_bus_param = ov772x_set_bus_param,
.query_bus_param = ov772x_query_bus_param,
.controls = ov772x_controls,
.num_controls = ARRAY_SIZE(ov772x_controls),
- .get_control = ov772x_get_control,
- .set_control = ov772x_set_control,
- .get_chip_id = ov772x_get_chip_id,
+};
+
+static struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
+ .g_ctrl = ov772x_g_ctrl,
+ .s_ctrl = ov772x_s_ctrl,
+ .g_chip_ident = ov772x_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
- .get_register = ov772x_get_register,
- .set_register = ov772x_set_register,
+ .g_register = ov772x_g_register,
+ .s_register = ov772x_s_register,
#endif
};
+static struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
+ .s_stream = ov772x_s_stream,
+ .g_fmt = ov772x_g_fmt,
+ .s_fmt = ov772x_s_fmt,
+ .try_fmt = ov772x_try_fmt,
+ .cropcap = ov772x_cropcap,
+ .g_crop = ov772x_g_crop,
+};
+
+static struct v4l2_subdev_ops ov772x_subdev_ops = {
+ .core = &ov772x_subdev_core_ops,
+ .video = &ov772x_subdev_video_ops,
+};
+
/*
* i2c_driver function
*/
@@ -1062,20 +1131,26 @@ static struct soc_camera_ops ov772x_ops = {
static int ov772x_probe(struct i2c_client *client)
#else
static int ov772x_probe(struct i2c_client *client,
- const struct i2c_device_id *did)
+ const struct i2c_device_id *did)
#endif
{
struct ov772x_priv *priv;
struct ov772x_camera_info *info;
- struct soc_camera_device *icd;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl;
int ret;
- if (!client->dev.platform_data)
+ if (!icd) {
+ dev_err(&client->dev, "OV772X: missing soc-camera data!\n");
return -EINVAL;
+ }
- info = container_of(client->dev.platform_data,
- struct ov772x_camera_info, link);
+ icl = to_soc_camera_link(icd);
+ if (!icl)
+ return -EINVAL;
+
+ info = container_of(icl, struct ov772x_camera_info, link);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&adapter->dev,
@@ -1088,20 +1163,15 @@ static int ov772x_probe(struct i2c_client *client,
if (!priv)
return -ENOMEM;
- priv->info = info;
- priv->client = client;
- i2c_set_clientdata(client, priv);
+ priv->info = info;
- icd = &priv->icd;
- icd->ops = &ov772x_ops;
- icd->control = &client->dev;
- icd->width_max = MAX_WIDTH;
- icd->height_max = MAX_HEIGHT;
- icd->iface = priv->info->link.bus_id;
+ v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops);
- ret = soc_camera_device_register(icd);
+ icd->ops = &ov772x_ops;
+ ret = ov772x_video_probe(icd, client);
if (ret) {
+ icd->ops = NULL;
i2c_set_clientdata(client, NULL);
kfree(priv);
}
@@ -1111,9 +1181,10 @@ static int ov772x_probe(struct i2c_client *client,
static int ov772x_remove(struct i2c_client *client)
{
- struct ov772x_priv *priv = i2c_get_clientdata(client);
+ struct ov772x_priv *priv = to_ov772x(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
- soc_camera_device_unregister(&priv->icd);
+ icd->ops = NULL;
i2c_set_clientdata(client, NULL);
kfree(priv);
return 0;
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
index 42875ec21..d8c9e6632 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c
@@ -66,9 +66,10 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
u32 input;
pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo");
+ sp = (sid < ARRAY_SIZE(routing_schemes)) ?
+ routing_schemes[sid] : NULL;
- if ((sid < ARRAY_SIZE(routing_schemes)) &&
- ((sp = routing_schemes[sid]) != NULL) &&
+ if ((sp != NULL) &&
(hdw->input_val >= 0) &&
(hdw->input_val < sp->cnt)) {
input = sp->def[hdw->input_val];
diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c
index f695c6624..788de22df 100644
--- a/linux/drivers/media/video/pwc/pwc-if.c
+++ b/linux/drivers/media/video/pwc/pwc-if.c
@@ -1065,7 +1065,8 @@ static int pwc_create_sysfs_files(struct video_device *vdev)
goto err;
if (pdev->features & FEATURE_MOTOR_PANTILT) {
rc = device_create_file(&vdev->dev, &dev_attr_pan_tilt);
- if (rc) goto err_button;
+ if (rc)
+ goto err_button;
}
return 0;
@@ -1080,6 +1081,7 @@ err:
static void pwc_remove_sysfs_files(struct video_device *vdev)
{
struct pwc_device *pdev = video_get_drvdata(vdev);
+
if (pdev->features & FEATURE_MOTOR_PANTILT)
device_remove_file(&vdev->dev, &dev_attr_pan_tilt);
device_remove_file(&vdev->dev, &dev_attr_button);
@@ -1237,13 +1239,11 @@ static void pwc_cleanup(struct pwc_device *pdev)
video_unregister_device(pdev->vdev);
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
- if (pdev->button_dev) {
+ if (pdev->button_dev)
input_unregister_device(pdev->button_dev);
- input_free_device(pdev->button_dev);
- kfree(pdev->button_dev->phys);
- pdev->button_dev = NULL;
- }
#endif
+
+ kfree(pdev);
}
/* Note that all cleanup is done in the reverse order as in _open */
@@ -1289,8 +1289,6 @@ static int pwc_video_close(struct file *file)
PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen);
} else {
pwc_cleanup(pdev);
- /* Free memory (don't set pdev to 0 just yet) */
- kfree(pdev);
/* search device_hint[] table if we occupy a slot, by any chance */
for (hint = 0; hint < MAX_DEV_HINTS; hint++)
if (device_hint[hint].pdev == pdev)
@@ -1507,13 +1505,10 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
struct usb_device *udev = interface_to_usbdev(intf);
struct pwc_device *pdev = NULL;
int vendor_id, product_id, type_id;
- int i, hint, rc;
+ int hint, rc;
int features = 0;
int video_nr = -1; /* default: use next available device */
char serial_number[30], *name;
-#ifdef CONFIG_USB_PWC_INPUT_EVDEV
- char *phys = NULL;
-#endif
vendor_id = le16_to_cpu(udev->descriptor.idVendor);
product_id = le16_to_cpu(udev->descriptor.idProduct);
@@ -1765,8 +1760,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->vframes = default_fps;
strcpy(pdev->serial, serial_number);
pdev->features = features;
- if (vendor_id == 0x046D && product_id == 0x08B5)
- {
+ if (vendor_id == 0x046D && product_id == 0x08B5) {
/* Logitech QuickCam Orbit
The ranges have been determined experimentally; they may differ from cam to cam.
Also, the exact ranges left-right and up-down are different for my cam
@@ -1788,8 +1782,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->vdev = video_device_alloc();
if (!pdev->vdev) {
PWC_ERROR("Err, cannot allocate video_device struture. Failing probe.");
- kfree(pdev);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto err_free_mem;
}
memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template));
pdev->vdev->parent = &intf->dev;
@@ -1814,25 +1808,23 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
}
pdev->vdev->release = video_device_release;
- i = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
- if (i < 0) {
- PWC_ERROR("Failed to register as video device (%d).\n", i);
- rc = i;
- goto err;
- }
- else {
- PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num);
+ rc = video_register_device(pdev->vdev, VFL_TYPE_GRABBER, video_nr);
+ if (rc < 0) {
+ PWC_ERROR("Failed to register as video device (%d).\n", rc);
+ goto err_video_release;
}
+ PWC_INFO("Registered as /dev/video%d.\n", pdev->vdev->num);
+
/* occupy slot */
if (hint < MAX_DEV_HINTS)
device_hint[hint].pdev = pdev;
PWC_DEBUG_PROBE("probe() function returning struct at 0x%p.\n", pdev);
- usb_set_intfdata (intf, pdev);
+ usb_set_intfdata(intf, pdev);
rc = pwc_create_sysfs_files(pdev->vdev);
if (rc)
- goto err_unreg;
+ goto err_video_unreg;
/* Set the leds off */
pwc_set_leds(pdev, 0, 0);
@@ -1843,16 +1835,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
pdev->button_dev = input_allocate_device();
if (!pdev->button_dev) {
PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
- return -ENOMEM;
+ rc = -ENOMEM;
+ pwc_remove_sysfs_files(pdev->vdev);
+ goto err_video_unreg;
}
+ usb_make_path(udev, pdev->button_phys, sizeof(pdev->button_phys));
+ strlcat(pdev->button_phys, "/input0", sizeof(pdev->button_phys));
+
pdev->button_dev->name = "PWC snapshot button";
- phys = kasprintf(GFP_KERNEL,"usb-%s-%s", pdev->udev->bus->bus_name, pdev->udev->devpath);
- if (!phys) {
- input_free_device(pdev->button_dev);
- return -ENOMEM;
- }
- pdev->button_dev->phys = phys;
+ pdev->button_dev->phys = pdev->button_phys;
usb_to_input_id(pdev->udev, &pdev->button_dev->id);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
pdev->button_dev->dev.parent = &pdev->udev->dev;
@@ -1865,25 +1857,27 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
rc = input_register_device(pdev->button_dev);
if (rc) {
input_free_device(pdev->button_dev);
- kfree(pdev->button_dev->phys);
pdev->button_dev = NULL;
- return rc;
+ pwc_remove_sysfs_files(pdev->vdev);
+ goto err_video_unreg;
}
#endif
return 0;
-err_unreg:
+err_video_unreg:
if (hint < MAX_DEV_HINTS)
device_hint[hint].pdev = NULL;
video_unregister_device(pdev->vdev);
-err:
- video_device_release(pdev->vdev); /* Drip... drip... drip... */
- kfree(pdev); /* Oops, no memory leaks please */
+ pdev->vdev = NULL; /* So we don't try to release it below */
+err_video_release:
+ video_device_release(pdev->vdev);
+err_free_mem:
+ kfree(pdev);
return rc;
}
-/* The user janked out the cable... */
+/* The user yanked out the cable... */
static void usb_pwc_disconnect(struct usb_interface *intf)
{
struct pwc_device *pdev;
@@ -1914,7 +1908,7 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
/* Alert waiting processes */
wake_up_interruptible(&pdev->frameq);
/* Wait until device is closed */
- if(pdev->vopen) {
+ if (pdev->vopen) {
mutex_lock(&pdev->modlock);
pdev->unplugged = 1;
mutex_unlock(&pdev->modlock);
@@ -1923,8 +1917,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf)
/* Device is closed, so we can safely unregister it */
PWC_DEBUG_PROBE("Unregistering video device in disconnect().\n");
pwc_cleanup(pdev);
- /* Free memory (don't set pdev to 0 just yet) */
- kfree(pdev);
disconnect_out:
/* search device_hint[] table if we occupy a slot, by any chance */
diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c
index 2876ce084..bdb4ced57 100644
--- a/linux/drivers/media/video/pwc/pwc-v4l.c
+++ b/linux/drivers/media/video/pwc/pwc-v4l.c
@@ -1033,7 +1033,7 @@ long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (std->index != 0)
return -EINVAL;
std->id = V4L2_STD_UNKNOWN;
- strncpy(std->name, "webcam", sizeof(std->name));
+ strlcpy(std->name, "webcam", sizeof(std->name));
return 0;
}
diff --git a/linux/drivers/media/video/pwc/pwc.h b/linux/drivers/media/video/pwc/pwc.h
index 12c869d71..94c8e5a69 100644
--- a/linux/drivers/media/video/pwc/pwc.h
+++ b/linux/drivers/media/video/pwc/pwc.h
@@ -254,6 +254,7 @@ struct pwc_device
int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */
#ifdef CONFIG_USB_PWC_INPUT_EVDEV
struct input_dev *button_dev; /* webcam snapshot button input */
+ char button_phys[64];
#endif
/*** Misc. data ***/
diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c
index 3f1b63e4e..8ab1a5792 100644
--- a/linux/drivers/media/video/pxa_camera.c
+++ b/linux/drivers/media/video/pxa_camera.c
@@ -237,6 +237,10 @@ struct pxa_camera_dev {
u32 save_cicr[5];
};
+struct pxa_cam {
+ unsigned long flags;
+};
+
static const char *pxa_cam_driver_description = "PXA_Camera";
static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
@@ -249,9 +253,9 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
{
struct soc_camera_device *icd = vq->priv_data;
- dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
- *size = roundup(icd->width * icd->height *
+ *size = roundup(icd->user_width * icd->user_height *
((icd->current_fmt->depth + 7) >> 3), 8);
if (0 == *count)
@@ -271,7 +275,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
BUG_ON(in_interrupt());
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
&buf->vb, buf->vb.baddr, buf->vb.bsize);
/* This waits until this buffer is out of danger, i.e., until it is no
@@ -282,7 +286,8 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf)
for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) {
if (buf->dmas[i].sg_cpu)
- dma_free_coherent(ici->dev, buf->dmas[i].sg_size,
+ dma_free_coherent(ici->v4l2_dev.dev,
+ buf->dmas[i].sg_size,
buf->dmas[i].sg_cpu,
buf->dmas[i].sg_dma);
buf->dmas[i].sg_cpu = NULL;
@@ -337,19 +342,20 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
struct scatterlist **sg_first, int *sg_first_ofs)
{
struct pxa_cam_dma *pxa_dma = &buf->dmas[channel];
+ struct device *dev = pcdev->soc_host.v4l2_dev.dev;
struct scatterlist *sg;
int i, offset, sglen;
int dma_len = 0, xfer_len = 0;
if (pxa_dma->sg_cpu)
- dma_free_coherent(pcdev->soc_host.dev, pxa_dma->sg_size,
+ dma_free_coherent(dev, pxa_dma->sg_size,
pxa_dma->sg_cpu, pxa_dma->sg_dma);
sglen = calculate_dma_sglen(*sg_first, dma->sglen,
*sg_first_ofs, size);
pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc);
- pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->soc_host.dev, pxa_dma->sg_size,
+ pxa_dma->sg_cpu = dma_alloc_coherent(dev, pxa_dma->sg_size,
&pxa_dma->sg_dma, GFP_KERNEL);
if (!pxa_dma->sg_cpu)
return -ENOMEM;
@@ -357,7 +363,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
pxa_dma->sglen = sglen;
offset = *sg_first_ofs;
- dev_dbg(pcdev->soc_host.dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n",
+ dev_dbg(dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n",
*sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma);
@@ -380,7 +386,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev,
pxa_dma->sg_cpu[i].ddadr =
pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc);
- dev_vdbg(pcdev->soc_host.dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n",
+ dev_vdbg(dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n",
pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc),
sg_dma_address(sg) + offset, xfer_len);
offset = 0;
@@ -430,11 +436,12 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
struct soc_camera_device *icd = vq->priv_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
+ struct device *dev = pcdev->soc_host.v4l2_dev.dev;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
int ret;
int size_y, size_u = 0, size_v = 0;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
/* Added list head initialization on alloc */
@@ -453,12 +460,12 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
buf->inwork = 1;
if (buf->fmt != icd->current_fmt ||
- vb->width != icd->width ||
- vb->height != icd->height ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height ||
vb->field != field) {
buf->fmt = icd->current_fmt;
- vb->width = icd->width;
- vb->height = icd->height;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
@@ -492,8 +499,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y,
&sg, &next_ofs);
if (ret) {
- dev_err(pcdev->soc_host.dev,
- "DMA initialization for Y/RGB failed\n");
+ dev_err(dev, "DMA initialization for Y/RGB failed\n");
goto fail;
}
@@ -502,8 +508,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1,
size_u, &sg, &next_ofs);
if (ret) {
- dev_err(pcdev->soc_host.dev,
- "DMA initialization for U failed\n");
+ dev_err(dev, "DMA initialization for U failed\n");
goto fail_u;
}
@@ -512,8 +517,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2,
size_v, &sg, &next_ofs);
if (ret) {
- dev_err(pcdev->soc_host.dev,
- "DMA initialization for V failed\n");
+ dev_err(dev, "DMA initialization for V failed\n");
goto fail_v;
}
@@ -526,10 +530,10 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
return 0;
fail_v:
- dma_free_coherent(pcdev->soc_host.dev, buf->dmas[1].sg_size,
+ dma_free_coherent(dev, buf->dmas[1].sg_size,
buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma);
fail_u:
- dma_free_coherent(pcdev->soc_host.dev, buf->dmas[0].sg_size,
+ dma_free_coherent(dev, buf->dmas[0].sg_size,
buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma);
fail:
free_buffer(vq, buf);
@@ -553,7 +557,8 @@ static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev)
active = pcdev->active;
for (i = 0; i < pcdev->channels; i++) {
- dev_dbg(pcdev->soc_host.dev, "%s (channel=%d) ddadr=%08x\n", __func__,
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s (channel=%d) ddadr=%08x\n", __func__,
i, active->dmas[i].sg_dma);
DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma;
DCSR(pcdev->dma_chans[i]) = DCSR_RUN;
@@ -565,7 +570,8 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev)
int i;
for (i = 0; i < pcdev->channels; i++) {
- dev_dbg(pcdev->soc_host.dev, "%s (channel=%d)\n", __func__, i);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s (channel=%d)\n", __func__, i);
DCSR(pcdev->dma_chans[i]) = 0;
}
}
@@ -601,7 +607,7 @@ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev)
{
unsigned long cicr0, cifr;
- dev_dbg(pcdev->soc_host.dev, "%s\n", __func__);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__);
/* Reset the FIFOs */
cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F;
__raw_writel(cifr, pcdev->base + CIFR);
@@ -621,9 +627,10 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev)
__raw_writel(cicr0, pcdev->base + CICR0);
pcdev->active = NULL;
- dev_dbg(pcdev->soc_host.dev, "%s\n", __func__);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s\n", __func__);
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
@@ -631,12 +638,9 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
- unsigned long flags;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__,
- vb, vb->baddr, vb->bsize, pcdev->active);
-
- spin_lock_irqsave(&pcdev->lock, flags);
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %d active=%p\n",
+ __func__, vb, vb->baddr, vb->bsize, pcdev->active);
list_add_tail(&vb->queue, &pcdev->capture);
@@ -645,8 +649,6 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq,
if (!pcdev->active)
pxa_camera_start_capture(pcdev);
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void pxa_videobuf_release(struct videobuf_queue *vq,
@@ -655,22 +657,23 @@ static void pxa_videobuf_release(struct videobuf_queue *vq,
struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
#ifdef DEBUG
struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->dev.parent;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
vb, vb->baddr, vb->bsize);
switch (vb->state) {
case VIDEOBUF_ACTIVE:
- dev_dbg(&icd->dev, "%s (active)\n", __func__);
+ dev_dbg(dev, "%s (active)\n", __func__);
break;
case VIDEOBUF_QUEUED:
- dev_dbg(&icd->dev, "%s (queued)\n", __func__);
+ dev_dbg(dev, "%s (queued)\n", __func__);
break;
case VIDEOBUF_PREPARED:
- dev_dbg(&icd->dev, "%s (prepared)\n", __func__);
+ dev_dbg(dev, "%s (prepared)\n", __func__);
break;
default:
- dev_dbg(&icd->dev, "%s (unknown)\n", __func__);
+ dev_dbg(dev, "%s (unknown)\n", __func__);
break;
}
#endif
@@ -690,7 +693,8 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev,
do_gettimeofday(&vb->ts);
vb->field_count++;
wake_up(&vb->done);
- dev_dbg(pcdev->soc_host.dev, "%s dequeud buffer (vb=0x%p)\n", __func__, vb);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev, "%s dequeud buffer (vb=0x%p)\n",
+ __func__, vb);
if (list_empty(&pcdev->capture)) {
pxa_camera_stop_capture(pcdev);
@@ -726,7 +730,8 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev)
for (i = 0; i < pcdev->channels; i++)
if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP)
is_dma_stopped = 0;
- dev_dbg(pcdev->soc_host.dev, "%s : top queued buffer=%p, dma_stopped=%d\n",
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "%s : top queued buffer=%p, dma_stopped=%d\n",
__func__, pcdev->active, is_dma_stopped);
if (pcdev->active && is_dma_stopped)
pxa_camera_start_capture(pcdev);
@@ -735,6 +740,7 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev)
static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
enum pxa_camera_active_dma act_dma)
{
+ struct device *dev = pcdev->soc_host.v4l2_dev.dev;
struct pxa_buffer *buf;
unsigned long flags;
u32 status, camera_status, overrun;
@@ -751,13 +757,13 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
overrun |= CISR_IFO_1 | CISR_IFO_2;
if (status & DCSR_BUSERR) {
- dev_err(pcdev->soc_host.dev, "DMA Bus Error IRQ!\n");
+ dev_err(dev, "DMA Bus Error IRQ!\n");
goto out;
}
if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) {
- dev_err(pcdev->soc_host.dev, "Unknown DMA IRQ source, "
- "status: 0x%08x\n", status);
+ dev_err(dev, "Unknown DMA IRQ source, status: 0x%08x\n",
+ status);
goto out;
}
@@ -780,7 +786,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
buf = container_of(vb, struct pxa_buffer, vb);
WARN_ON(buf->inwork || list_empty(&vb->queue));
- dev_dbg(pcdev->soc_host.dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n",
+ dev_dbg(dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n",
__func__, channel, status & DCSR_STARTINTR ? "SOF " : "",
status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel));
@@ -791,7 +797,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev,
*/
if (camera_status & overrun &&
!list_is_last(pcdev->capture.next, &pcdev->capture)) {
- dev_dbg(pcdev->soc_host.dev, "FIFO overrun! CISR: %x\n",
+ dev_dbg(dev, "FIFO overrun! CISR: %x\n",
camera_status);
pxa_camera_stop_capture(pcdev);
pxa_camera_start_capture(pcdev);
@@ -846,9 +852,11 @@ static void pxa_camera_init_videobuf(struct videobuf_queue *q,
sizeof(struct pxa_buffer), icd);
}
-static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev)
+static u32 mclk_get_divisor(struct platform_device *pdev,
+ struct pxa_camera_dev *pcdev)
{
unsigned long mclk = pcdev->mclk;
+ struct device *dev = &pdev->dev;
u32 div;
unsigned long lcdclk;
@@ -858,7 +866,7 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev)
/* mclk <= ciclk / 4 (27.4.2) */
if (mclk > lcdclk / 4) {
mclk = lcdclk / 4;
- dev_warn(pcdev->soc_host.dev, "Limiting master clock to %lu\n", mclk);
+ dev_warn(dev, "Limiting master clock to %lu\n", mclk);
}
/* We verify mclk != 0, so if anyone breaks it, here comes their Oops */
@@ -868,8 +876,8 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev)
if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
pcdev->mclk = lcdclk / (2 * (div + 1));
- dev_dbg(pcdev->soc_host.dev, "LCD clock %luHz, target freq %luHz, "
- "divisor %u\n", lcdclk, mclk, div);
+ dev_dbg(dev, "LCD clock %luHz, target freq %luHz, divisor %u\n",
+ lcdclk, mclk, div);
return div;
}
@@ -886,14 +894,15 @@ static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev,
static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
{
struct pxacamera_platform_data *pdata = pcdev->pdata;
+ struct device *dev = pcdev->soc_host.v4l2_dev.dev;
u32 cicr4 = 0;
- dev_dbg(pcdev->soc_host.dev, "Registered platform device at %p data %p\n",
+ dev_dbg(dev, "Registered platform device at %p data %p\n",
pcdev, pdata);
if (pdata && pdata->init) {
- dev_dbg(pcdev->soc_host.dev, "%s: Init gpios\n", __func__);
- pdata->init(pcdev->soc_host.dev);
+ dev_dbg(dev, "%s: Init gpios\n", __func__);
+ pdata->init(dev);
}
/* disable all interrupts */
@@ -935,7 +944,8 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
struct videobuf_buffer *vb;
status = __raw_readl(pcdev->base + CISR);
- dev_dbg(pcdev->soc_host.dev, "Camera interrupt status 0x%lx\n", status);
+ dev_dbg(pcdev->soc_host.v4l2_dev.dev,
+ "Camera interrupt status 0x%lx\n", status);
if (!status)
return IRQ_NONE;
@@ -967,24 +977,18 @@ static int pxa_camera_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
- int ret;
- if (pcdev->icd) {
- ret = -EBUSY;
- goto ebusy;
- }
-
- dev_info(&icd->dev, "PXA Camera driver attached to camera %d\n",
- icd->devnum);
+ if (pcdev->icd)
+ return -EBUSY;
pxa_camera_activate(pcdev);
- ret = icd->ops->init(icd);
- if (!ret)
- pcdev->icd = icd;
+ pcdev->icd = icd;
-ebusy:
- return ret;
+ dev_info(icd->dev.parent, "PXA Camera driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
}
/* Called with .video_lock held */
@@ -995,7 +999,7 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
BUG_ON(icd != pcdev->icd);
- dev_info(&icd->dev, "PXA Camera driver detached from camera %d\n",
+ dev_info(icd->dev.parent, "PXA Camera driver detached from camera %d\n",
icd->devnum);
/* disable capture, disable interrupts */
@@ -1006,8 +1010,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd)
DCSR(pcdev->dma_chans[1]) = 0;
DCSR(pcdev->dma_chans[2]) = 0;
- icd->ops->release(icd);
-
pxa_camera_deactivate(pcdev);
pcdev->icd = NULL;
@@ -1055,57 +1057,17 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
return 0;
}
-static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
+static void pxa_camera_setup_cicr(struct soc_camera_device *icd,
+ unsigned long flags, __u32 pixfmt)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
- unsigned long dw, bpp, bus_flags, camera_flags, common_flags;
+ unsigned long dw, bpp;
u32 cicr0, cicr1, cicr2, cicr3, cicr4 = 0;
- int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
-
- if (ret < 0)
- return ret;
-
- camera_flags = icd->ops->query_bus_param(icd);
-
- common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
- if (!common_flags)
- return -EINVAL;
-
- pcdev->channels = 1;
-
- /* Make choises, based on platform preferences */
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
- if (pcdev->platform_flags & PXA_CAMERA_HSP)
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
- if (pcdev->platform_flags & PXA_CAMERA_VSP)
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
- else
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
- }
-
- if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
- (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
- if (pcdev->platform_flags & PXA_CAMERA_PCP)
- common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
- else
- common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
- }
-
- ret = icd->ops->set_bus_param(icd, common_flags);
- if (ret < 0)
- return ret;
/* Datawidth is now guaranteed to be equal to one of the three values.
* We fix bit-per-pixel equal to data-width... */
- switch (common_flags & SOCAM_DATAWIDTH_MASK) {
+ switch (flags & SOCAM_DATAWIDTH_MASK) {
case SOCAM_DATAWIDTH_10:
dw = 4;
bpp = 0x40;
@@ -1126,18 +1088,18 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
cicr4 |= CICR4_PCLK_EN;
if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN)
cicr4 |= CICR4_MCLK_EN;
- if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
+ if (flags & SOCAM_PCLK_SAMPLE_FALLING)
cicr4 |= CICR4_PCP;
- if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
+ if (flags & SOCAM_HSYNC_ACTIVE_LOW)
cicr4 |= CICR4_HSP;
- if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
+ if (flags & SOCAM_VSYNC_ACTIVE_LOW)
cicr4 |= CICR4_VSP;
cicr0 = __raw_readl(pcdev->base + CICR0);
if (cicr0 & CICR0_ENB)
__raw_writel(cicr0 & ~CICR0_ENB, pcdev->base + CICR0);
- cicr1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw;
+ cicr1 = CICR1_PPL_VAL(icd->user_width - 1) | bpp | dw;
switch (pixfmt) {
case V4L2_PIX_FMT_YUV422P:
@@ -1166,7 +1128,7 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
}
cicr2 = 0;
- cicr3 = CICR3_LPF_VAL(icd->height - 1) |
+ cicr3 = CICR3_LPF_VAL(icd->user_height - 1) |
CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top));
cicr4 |= pcdev->mclk_divisor;
@@ -1180,6 +1142,59 @@ static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
CICR0_SIM_MP : (CICR0_SL_CAP_EN | CICR0_SIM_SP));
cicr0 |= CICR0_DMAEN | CICR0_IRQ_MASK;
__raw_writel(cicr0, pcdev->base + CICR0);
+}
+
+static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+ unsigned long bus_flags, camera_flags, common_flags;
+ int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags);
+ struct pxa_cam *cam = icd->host_priv;
+
+ if (ret < 0)
+ return ret;
+
+ camera_flags = icd->ops->query_bus_param(icd);
+
+ common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
+ if (!common_flags)
+ return -EINVAL;
+
+ pcdev->channels = 1;
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & PXA_CAMERA_HSP)
+ common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
+ if (pcdev->platform_flags & PXA_CAMERA_VSP)
+ common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
+ (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
+ if (pcdev->platform_flags & PXA_CAMERA_PCP)
+ common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
+ else
+ common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
+ }
+
+ cam->flags = common_flags;
+
+ ret = icd->ops->set_bus_param(icd, common_flags);
+ if (ret < 0)
+ return ret;
+
+ pxa_camera_setup_cicr(icd, common_flags, pixfmt);
return 0;
}
@@ -1243,8 +1258,9 @@ static int required_buswidth(const struct soc_camera_data_format *fmt)
static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
struct soc_camera_format_xlate *xlate)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->dev.parent;
int formats = 0, buswidth, ret;
+ struct pxa_cam *cam;
buswidth = required_buswidth(icd->formats + idx);
@@ -1255,6 +1271,16 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
if (ret < 0)
return 0;
+ if (!icd->host_priv) {
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+
+ icd->host_priv = cam;
+ } else {
+ cam = icd->host_priv;
+ }
+
switch (icd->formats[idx].fourcc) {
case V4L2_PIX_FMT_UYVY:
formats++;
@@ -1263,7 +1289,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = buswidth;
xlate++;
- dev_dbg(ici->dev, "Providing format %s using %s\n",
+ dev_dbg(dev, "Providing format %s using %s\n",
pxa_camera_formats[0].name,
icd->formats[idx].name);
}
@@ -1278,7 +1304,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = buswidth;
xlate++;
- dev_dbg(ici->dev, "Providing format %s packed\n",
+ dev_dbg(dev, "Providing format %s packed\n",
icd->formats[idx].name);
}
break;
@@ -1290,7 +1316,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = icd->formats[idx].depth;
xlate++;
- dev_dbg(ici->dev,
+ dev_dbg(dev,
"Providing format %s in pass-through mode\n",
icd->formats[idx].name);
}
@@ -1299,31 +1325,80 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
return formats;
}
+static void pxa_camera_put_formats(struct soc_camera_device *icd)
+{
+ kfree(icd->host_priv);
+ icd->host_priv = NULL;
+}
+
+static int pxa_camera_check_frame(struct v4l2_pix_format *pix)
+{
+ /* limit to pxa hardware capabilities */
+ return pix->height < 32 || pix->height > 2048 || pix->width < 48 ||
+ pix->width > 2048 || (pix->width & 0x01);
+}
+
static int pxa_camera_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+ struct v4l2_crop *a)
{
+ struct v4l2_rect *rect = &a->c;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
+ struct device *dev = icd->dev.parent;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_sense sense = {
.master_clock = pcdev->mclk,
.pixel_clock_max = pcdev->ciclk / 4,
};
+ struct v4l2_format f;
+ struct v4l2_pix_format *pix = &f.fmt.pix, pix_tmp;
+ struct pxa_cam *cam = icd->host_priv;
int ret;
/* If PCLK is used to latch data from the sensor, check sense */
if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
icd->sense = &sense;
- ret = icd->ops->set_crop(icd, rect);
+ ret = v4l2_subdev_call(sd, video, s_crop, a);
icd->sense = NULL;
if (ret < 0) {
- dev_warn(ici->dev, "Failed to crop to %ux%u@%u:%u\n",
+ dev_warn(dev, "Failed to crop to %ux%u@%u:%u\n",
rect->width, rect->height, rect->left, rect->top);
- } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+ return ret;
+ }
+
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ if (ret < 0)
+ return ret;
+
+ pix_tmp = *pix;
+ if (pxa_camera_check_frame(pix)) {
+ /*
+ * Camera cropping produced a frame beyond our capabilities.
+ * FIXME: just extract a subframe, that we can process.
+ */
+ v4l_bound_align_image(&pix->width, 48, 2048, 1,
+ &pix->height, 32, 2048, 0,
+ icd->current_fmt->fourcc == V4L2_PIX_FMT_YUV422P ?
+ 4 : 0);
+ ret = v4l2_subdev_call(sd, video, s_fmt, &f);
+ if (ret < 0)
+ return ret;
+
+ if (pxa_camera_check_frame(pix)) {
+ dev_warn(icd->dev.parent,
+ "Inconsistent state. Use S_FMT to repair\n");
+ return -EINVAL;
+ }
+ }
+
+ if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
if (sense.pixel_clock > sense.pixel_clock_max) {
- dev_err(ici->dev,
+ dev_err(dev,
"pixel clock %lu set by the camera too high!",
sense.pixel_clock);
return -EIO;
@@ -1331,6 +1406,11 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd,
recalculate_fifo_timeout(pcdev, sense.pixel_clock);
}
+ icd->user_width = pix->width;
+ icd->user_height = pix->height;
+
+ pxa_camera_setup_cicr(icd, cam->flags, icd->current_fmt->fourcc);
+
return ret;
}
@@ -1339,6 +1419,8 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct pxa_camera_dev *pcdev = ici->priv;
+ struct device *dev = icd->dev.parent;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_data_format *cam_fmt = NULL;
const struct soc_camera_format_xlate *xlate = NULL;
struct soc_camera_sense sense = {
@@ -1351,7 +1433,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat);
+ dev_warn(dev, "Format %x not found\n", pix->pixelformat);
return -EINVAL;
}
@@ -1362,16 +1444,21 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
icd->sense = &sense;
cam_f.fmt.pix.pixelformat = cam_fmt->fourcc;
- ret = icd->ops->set_fmt(icd, &cam_f);
+ ret = v4l2_subdev_call(sd, video, s_fmt, f);
icd->sense = NULL;
if (ret < 0) {
- dev_warn(ici->dev, "Failed to configure for format %x\n",
+ dev_warn(dev, "Failed to configure for format %x\n",
pix->pixelformat);
+ } else if (pxa_camera_check_frame(pix)) {
+ dev_warn(dev,
+ "Camera driver produced an unsupported frame %dx%d\n",
+ pix->width, pix->height);
+ ret = -EINVAL;
} else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
if (sense.pixel_clock > sense.pixel_clock_max) {
- dev_err(ici->dev,
+ dev_err(dev,
"pixel clock %lu set by the camera too high!",
sense.pixel_clock);
return -EIO;
@@ -1391,6 +1478,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
__u32 pixfmt = pix->pixelformat;
@@ -1399,7 +1487,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pixfmt);
+ dev_warn(ici->v4l2_dev.dev, "Format %x not found\n", pixfmt);
return -EINVAL;
}
@@ -1411,7 +1499,7 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
*/
v4l_bound_align_image(&pix->width, 48, 2048, 1,
&pix->height, 32, 2048, 0,
- xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0);
+ pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
pix->bytesperline = pix->width *
DIV_ROUND_UP(xlate->host_fmt->depth, 8);
@@ -1420,15 +1508,15 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd,
/* camera has to see its format, but the user the original one */
pix->pixelformat = xlate->cam_fmt->fourcc;
/* limit to sensor capabilities */
- ret = icd->ops->try_fmt(icd, f);
- pix->pixelformat = xlate->host_fmt->fourcc;
+ ret = v4l2_subdev_call(sd, video, try_fmt, f);
+ pix->pixelformat = pixfmt;
field = pix->field;
if (field == V4L2_FIELD_ANY) {
pix->field = V4L2_FIELD_NONE;
} else if (field != V4L2_FIELD_NONE) {
- dev_err(&icd->dev, "Field type %d unsupported.\n", field);
+ dev_err(icd->dev.parent, "Field type %d unsupported.\n", field);
return -EINVAL;
}
@@ -1534,6 +1622,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
.resume = pxa_camera_resume,
.set_crop = pxa_camera_set_crop,
.get_formats = pxa_camera_get_formats,
+ .put_formats = pxa_camera_put_formats,
.set_fmt = pxa_camera_set_fmt,
.try_fmt = pxa_camera_try_fmt,
.init_videobuf = pxa_camera_init_videobuf,
@@ -1591,8 +1680,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
pcdev->mclk = 20000000;
}
- pcdev->soc_host.dev = &pdev->dev;
- pcdev->mclk_divisor = mclk_get_divisor(pcdev);
+ pcdev->mclk_divisor = mclk_get_divisor(pdev, pcdev);
INIT_LIST_HEAD(&pcdev->capture);
spin_lock_init(&pcdev->lock);
@@ -1657,6 +1745,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev)
pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME;
pcdev->soc_host.ops = &pxa_soc_camera_host_ops;
pcdev->soc_host.priv = pcdev;
+ pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
err = soc_camera_host_register(&pcdev->soc_host);
@@ -1738,3 +1827,4 @@ module_exit(pxa_camera_exit);
MODULE_DESCRIPTION("PXA27x SoC Camera Host driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" PXA_CAM_DRV_NAME);
diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig
index 5bcce092e..22bfd62c9 100644
--- a/linux/drivers/media/video/saa7134/Kconfig
+++ b/linux/drivers/media/video/saa7134/Kconfig
@@ -47,6 +47,7 @@ config VIDEO_SAA7134_DVB
select DVB_TDA10048 if !DVB_FE_CUSTOMISE
select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
+ select DVB_ZL10039 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB cards based on the
Philips saa7134 chip.
diff --git a/linux/drivers/media/video/saa7134/saa6752hs.c b/linux/drivers/media/video/saa7134/saa6752hs.c
index 860f271dd..32ecebe8c 100644
--- a/linux/drivers/media/video/saa7134/saa6752hs.c
+++ b/linux/drivers/media/video/saa7134/saa6752hs.c
@@ -475,7 +475,7 @@ static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
(!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3))
return -ERANGE;
- new = old;
+ params->au_encoding = new;
break;
case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
old = params->au_l2_bitrate;
diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c
index c09ec3e6f..504186ae6 100644
--- a/linux/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c
@@ -440,6 +440,16 @@ snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream)
/*
* ALSA hardware capabilities definition
+ *
+ * Report only 32kHz for ALSA:
+ *
+ * - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz
+ * only
+ * - SAA7134 for TV mode uses DemDec mode (32kHz)
+ * - Radio works in 32kHz only
+ * - When recording 48kHz from Line1/Line2, switching of capture source to TV
+ * means
+ * switching to 32kHz without any frequency translation
*/
static struct snd_pcm_hardware snd_card_saa7134_capture =
@@ -453,9 +463,9 @@ static struct snd_pcm_hardware snd_card_saa7134_capture =
SNDRV_PCM_FMTBIT_U8 | \
SNDRV_PCM_FMTBIT_U16_LE | \
SNDRV_PCM_FMTBIT_U16_BE,
- .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+ .rates = SNDRV_PCM_RATE_32000,
.rate_min = 32000,
- .rate_max = 48000,
+ .rate_max = 32000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (256*1024),
diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c
index 22fdc40d1..c401a37ea 100644
--- a/linux/drivers/media/video/saa7134/saa7134-cards.c
+++ b/linux/drivers/media/video/saa7134/saa7134-cards.c
@@ -1633,7 +1633,7 @@ struct saa7134_board saa7134_boards[] = {
}},
.radio = {
.name = name_radio,
- .amux = LINE1,
+ .amux = TV,
.gpio = 0x00300001,
},
.mute = {
@@ -3702,8 +3702,8 @@ struct saa7134_board saa7134_boards[] = {
.amux = TV,
.gpio = 0x0200000,
},
- },
- [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = {
+ },
+ [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = {
.name = "ASUSTeK P7131 Analog",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_TDA8290,
@@ -4120,6 +4120,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
@@ -4184,6 +4185,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
@@ -4214,6 +4216,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.gpiomask = 0x00008000,
.inputs = {{
@@ -4389,6 +4392,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
.name = name_tv,
@@ -4417,6 +4421,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
.name = name_tv,
@@ -4445,6 +4450,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
.name = name_tv,
@@ -4473,6 +4479,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.tda9887_conf = TDA9887_PRESENT,
.inputs = {{
.name = name_tv,
@@ -4579,6 +4586,7 @@ struct saa7134_board saa7134_boards[] = {
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
+ .rds_addr = 0x10,
.empress_addr = 0x20,
.tda9887_conf = TDA9887_PRESENT,
.inputs = { {
@@ -4900,7 +4908,7 @@ struct saa7134_board saa7134_boards[] = {
/* Igor Kuznetsov <igk@igk.ru> */
.name = "Beholder BeholdTV H6",
.audio_clock = 0x00187de7,
- .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
@@ -5155,6 +5163,25 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x00,
},
},
+ [SAA7134_BOARD_VIDEOMATE_S350] = {
+ /* Jan D. Louw <jd.louw@mweb.co.za */
+ .name = "Compro VideoMate S350/S300",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8, /* Not tested */
+ .amux = LINE1
+ } },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -6262,7 +6289,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subvendor = 0x1461, /* Avermedia Technologies Inc */
.subdevice = 0xf31d,
.driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS,
-
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x185b,
+ .subdevice = 0xc900,
+ .driver_data = SAA7134_BOARD_VIDEOMATE_S350,
}, {
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
@@ -6599,7 +6631,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_FLYDVBT_LR301:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
- case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
+ case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
case SAA7134_BOARD_FLYDVBTDUO:
case SAA7134_BOARD_PROTEUS_2309:
case SAA7134_BOARD_AVERMEDIA_A16AR:
@@ -6776,6 +6808,11 @@ int saa7134_board_init1(struct saa7134_dev *dev)
saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100);
saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100);
break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000);
+ break;
}
return 0;
}
diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c
index a5263c391..18223dafe 100644
--- a/linux/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c
@@ -56,6 +56,7 @@
#include "zl10353.h"
#include "zl10036.h"
+#include "zl10039.h"
#include "mt312.h"
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
@@ -968,6 +969,10 @@ static struct zl10036_config avertv_a700_tuner = {
.tuner_address = 0x60,
};
+static struct mt312_config zl10313_compro_s350_config = {
+ .demod_address = 0x0e,
+};
+
static struct lgdt3305_config hcw_lgdt3305_config = {
.i2c_addr = 0x0e,
.mpeg_mode = LGDT3305_MPEG_SERIAL,
@@ -1461,7 +1466,7 @@ static int dvb_init(struct saa7134_dev *dev)
if (fe0->dvb.frontend) {
dvb_attach(simple_tuner_attach, fe0->dvb.frontend,
&dev->i2c_adap, 0x61,
- TUNER_PHILIPS_FMD1216ME_MK3);
+ TUNER_PHILIPS_FMD1216MEX_MK3);
}
break;
case SAA7134_BOARD_AVERMEDIA_A700_PRO:
@@ -1477,6 +1482,16 @@ static int dvb_init(struct saa7134_dev *dev)
}
}
break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ fe0->dvb.frontend = dvb_attach(mt312_attach,
+ &zl10313_compro_s350_config, &dev->i2c_adap);
+ if (fe0->dvb.frontend)
+ if (dvb_attach(zl10039_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap) == NULL)
+ wprintk("%s: No zl10039 found!\n",
+ __func__);
+
+ break;
default:
wprintk("Huh? unknown DVB card?\n");
break;
diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c
index b54bfd645..146ac816a 100644
--- a/linux/drivers/media/video/saa7134/saa7134-input.c
+++ b/linux/drivers/media/video/saa7134/saa7134-input.c
@@ -605,7 +605,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA:
- case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
+ case SAA7134_BOARD_ASUSTeK_P7131_ANALOG:
ir_codes = ir_codes_asus_pc39;
mask_keydown = 0x0040000;
rc5_gpio = 1;
@@ -646,6 +646,11 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keycode = 0x7f;
polling = 40; /* ms */
break;
+ case SAA7134_BOARD_VIDEOMATE_S350:
+ ir_codes = ir_codes_videomate_s350;
+ mask_keycode = 0x003f00;
+ mask_keydown = 0x040000;
+ break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
@@ -782,6 +787,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
#else
init_data.get_key = get_key_pinnacle_color;
init_data.ir_codes = ir_codes_pinnacle_color;
+ info.addr = 0x47;
#endif
} else {
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
@@ -790,6 +796,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
#else
init_data.get_key = get_key_pinnacle_grey;
init_data.ir_codes = ir_codes_pinnacle_grey;
+ info.addr = 0x47;
#endif
}
break;
diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h
index 6b0742ad7..c0757f8a1 100644
--- a/linux/drivers/media/video/saa7134/saa7134.h
+++ b/linux/drivers/media/video/saa7134/saa7134.h
@@ -293,6 +293,7 @@ struct saa7134_format {
#define SAA7134_BOARD_BEHOLD_607RDS_MK5 166
#define SAA7134_BOARD_BEHOLD_609RDS_MK3 167
#define SAA7134_BOARD_BEHOLD_609RDS_MK5 168
+#define SAA7134_BOARD_VIDEOMATE_S350 169
#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c
index 7ea2a0edf..d35f66d8a 100644
--- a/linux/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c
@@ -75,6 +75,13 @@
#define CDBYR2 0x98 /* Capture data bottom-field address Y register 2 */
#define CDBCR2 0x9c /* Capture data bottom-field address C register 2 */
+#undef DEBUG_GEOMETRY
+#ifdef DEBUG_GEOMETRY
+#define dev_geo dev_info
+#else
+#define dev_geo dev_dbg
+#endif
+
/* per video frame buffer */
struct sh_mobile_ceu_buffer {
struct videobuf_buffer vb; /* v4l buffer must be first */
@@ -94,10 +101,21 @@ struct sh_mobile_ceu_dev {
spinlock_t lock;
struct list_head capture;
struct videobuf_buffer *active;
- int is_interlaced;
struct sh_mobile_ceu_info *pdata;
+ u32 cflcr;
+
+ unsigned int is_interlaced:1;
+ unsigned int image_mode:1;
+ unsigned int is_16bit:1;
+};
+
+struct sh_mobile_ceu_cam {
+ struct v4l2_rect ceu_rect;
+ unsigned int cam_width;
+ unsigned int cam_height;
+ const struct soc_camera_data_format *extra_fmt;
const struct soc_camera_data_format *camera_fmt;
};
@@ -148,7 +166,8 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq,
struct sh_mobile_ceu_dev *pcdev = ici->priv;
int bytes_per_pixel = (icd->current_fmt->depth + 7) >> 3;
- *size = PAGE_ALIGN(icd->width * icd->height * bytes_per_pixel);
+ *size = PAGE_ALIGN(icd->user_width * icd->user_height *
+ bytes_per_pixel);
if (0 == *count)
*count = 2;
@@ -158,7 +177,7 @@ static int sh_mobile_ceu_videobuf_setup(struct videobuf_queue *vq,
(*count)--;
}
- dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->dev.parent, "count=%d, size=%d\n", *count, *size);
return 0;
}
@@ -167,8 +186,9 @@ static void free_buffer(struct videobuf_queue *vq,
struct sh_mobile_ceu_buffer *buf)
{
struct soc_camera_device *icd = vq->priv_data;
+ struct device *dev = icd->dev.parent;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
+ dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
&buf->vb, buf->vb.baddr, buf->vb.bsize);
if (in_interrupt())
@@ -176,7 +196,7 @@ static void free_buffer(struct videobuf_queue *vq,
videobuf_waiton(&buf->vb, 0, 0);
videobuf_dma_contig_free(vq, &buf->vb);
- dev_dbg(&icd->dev, "%s freed\n", __func__);
+ dev_dbg(dev, "%s freed\n", __func__);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
@@ -207,7 +227,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
phys_addr_top = videobuf_to_dma_contig(pcdev->active);
ceu_write(pcdev, CDAYR, phys_addr_top);
if (pcdev->is_interlaced) {
- phys_addr_bottom = phys_addr_top + icd->width;
+ phys_addr_bottom = phys_addr_top + icd->user_width;
ceu_write(pcdev, CDBYR, phys_addr_bottom);
}
@@ -216,10 +236,12 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
- phys_addr_top += icd->width * icd->height;
+ phys_addr_top += icd->user_width *
+ icd->user_height;
ceu_write(pcdev, CDACR, phys_addr_top);
if (pcdev->is_interlaced) {
- phys_addr_bottom = phys_addr_top + icd->width;
+ phys_addr_bottom = phys_addr_top +
+ icd->user_width;
ceu_write(pcdev, CDBCR, phys_addr_bottom);
}
}
@@ -238,7 +260,7 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
buf = container_of(vb, struct sh_mobile_ceu_buffer, vb);
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
vb, vb->baddr, vb->bsize);
/* Added list head initialization on alloc */
@@ -253,12 +275,12 @@ static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq,
BUG_ON(NULL == icd->current_fmt);
if (buf->fmt != icd->current_fmt ||
- vb->width != icd->width ||
- vb->height != icd->height ||
+ vb->width != icd->user_width ||
+ vb->height != icd->user_height ||
vb->field != field) {
buf->fmt = icd->current_fmt;
- vb->width = icd->width;
- vb->height = icd->height;
+ vb->width = icd->user_width;
+ vb->height = icd->user_height;
vb->field = field;
vb->state = VIDEOBUF_NEEDS_INIT;
}
@@ -283,32 +305,50 @@ out:
return ret;
}
+/* Called under spinlock_irqsave(&pcdev->lock, ...) */
static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
struct soc_camera_device *icd = vq->priv_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- unsigned long flags;
- dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
+ dev_dbg(icd->dev.parent, "%s (vb=0x%p) 0x%08lx %zd\n", __func__,
vb, vb->baddr, vb->bsize);
vb->state = VIDEOBUF_QUEUED;
- spin_lock_irqsave(&pcdev->lock, flags);
list_add_tail(&vb->queue, &pcdev->capture);
if (!pcdev->active) {
pcdev->active = vb;
sh_mobile_ceu_capture(pcdev);
}
-
- spin_unlock_irqrestore(&pcdev->lock, flags);
}
static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq,
struct videobuf_buffer *vb)
{
+ struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ if (pcdev->active == vb) {
+ /* disable capture (release DMA buffer), reset */
+ ceu_write(pcdev, CAPSR, 1 << 16);
+ pcdev->active = NULL;
+ }
+
+ if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
+ !list_empty(&vb->queue)) {
+ vb->state = VIDEOBUF_ERROR;
+ list_del_init(&vb->queue);
+ }
+
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+
free_buffer(vq, container_of(vb, struct sh_mobile_ceu_buffer, vb));
}
@@ -328,6 +368,10 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
spin_lock_irqsave(&pcdev->lock, flags);
vb = pcdev->active;
+ if (!vb)
+ /* Stale interrupt from a released buffer */
+ goto out;
+
list_del_init(&vb->queue);
if (!list_empty(&pcdev->capture))
@@ -342,6 +386,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data)
do_gettimeofday(&vb->ts);
vb->field_count++;
wake_up(&vb->done);
+
+out:
spin_unlock_irqrestore(&pcdev->lock, flags);
return IRQ_HANDLED;
@@ -352,19 +398,14 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret = -EBUSY;
if (pcdev->icd)
- goto err;
+ return -EBUSY;
- dev_info(&icd->dev,
+ dev_info(icd->dev.parent,
"SuperH Mobile CEU driver attached to camera %d\n",
icd->devnum);
- ret = icd->ops->init(icd);
- if (ret)
- goto err;
-
clk_enable(pcdev->clk);
ceu_write(pcdev, CAPSR, 1 << 16); /* reset */
@@ -372,8 +413,8 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd)
msleep(1);
pcdev->icd = icd;
-err:
- return ret;
+
+ return 0;
}
/* Called with .video_lock held */
@@ -401,23 +442,149 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
clk_disable(pcdev->clk);
- icd->ops->release(icd);
-
- dev_info(&icd->dev,
+ dev_info(icd->dev.parent,
"SuperH Mobile CEU driver detached from camera %d\n",
icd->devnum);
pcdev->icd = NULL;
}
+/*
+ * See chapter 29.4.12 "Capture Filter Control Register (CFLCR)"
+ * in SH7722 Hardware Manual
+ */
+static unsigned int size_dst(unsigned int src, unsigned int scale)
+{
+ unsigned int mant_pre = scale >> 12;
+ if (!src || !scale)
+ return src;
+ return ((mant_pre + 2 * (src - 1)) / (2 * mant_pre) - 1) *
+ mant_pre * 4096 / scale + 1;
+}
+
+static u16 calc_scale(unsigned int src, unsigned int *dst)
+{
+ u16 scale;
+
+ if (src == *dst)
+ return 0;
+
+ scale = (src * 4096 / *dst) & ~7;
+
+ while (scale > 4096 && size_dst(src, scale) < *dst)
+ scale -= 8;
+
+ *dst = size_dst(src, scale);
+
+ return scale;
+}
+
+/* rect is guaranteed to not exceed the scaled camera rectangle */
+static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd,
+ unsigned int out_width,
+ unsigned int out_height)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_rect *rect = &cam->ceu_rect;
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ unsigned int height, width, cdwdr_width, in_width, in_height;
+ unsigned int left_offset, top_offset;
+ u32 camor;
+
+ dev_dbg(icd->dev.parent, "Crop %ux%u@%u:%u\n",
+ rect->width, rect->height, rect->left, rect->top);
+
+ left_offset = rect->left;
+ top_offset = rect->top;
+
+ if (pcdev->image_mode) {
+ in_width = rect->width;
+ if (!pcdev->is_16bit) {
+ in_width *= 2;
+ left_offset *= 2;
+ }
+ width = cdwdr_width = out_width;
+ } else {
+ unsigned int w_factor = (icd->current_fmt->depth + 7) >> 3;
+
+ width = out_width * w_factor / 2;
+
+ if (!pcdev->is_16bit)
+ w_factor *= 2;
+
+ in_width = rect->width * w_factor / 2;
+ left_offset = left_offset * w_factor / 2;
+
+ cdwdr_width = width * 2;
+ }
+
+ height = out_height;
+ in_height = rect->height;
+ if (pcdev->is_interlaced) {
+ height /= 2;
+ in_height /= 2;
+ top_offset /= 2;
+ cdwdr_width *= 2;
+ }
+
+ /* Set CAMOR, CAPWR, CFSZR, take care of CDWDR */
+ camor = left_offset | (top_offset << 16);
+
+ dev_geo(icd->dev.parent,
+ "CAMOR 0x%x, CAPWR 0x%x, CFSZR 0x%x, CDWDR 0x%x\n", camor,
+ (in_height << 16) | in_width, (height << 16) | width,
+ cdwdr_width);
+
+ ceu_write(pcdev, CAMOR, camor);
+ ceu_write(pcdev, CAPWR, (in_height << 16) | in_width);
+ ceu_write(pcdev, CFSZR, (height << 16) | width);
+ ceu_write(pcdev, CDWDR, cdwdr_width);
+}
+
+static u32 capture_save_reset(struct sh_mobile_ceu_dev *pcdev)
+{
+ u32 capsr = ceu_read(pcdev, CAPSR);
+ ceu_write(pcdev, CAPSR, 1 << 16); /* reset, stop capture */
+ return capsr;
+}
+
+static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
+{
+ unsigned long timeout = jiffies + 10 * HZ;
+
+ /*
+ * Wait until the end of the current frame. It can take a long time,
+ * but if it has been aborted by a CAPSR reset, it shoule exit sooner.
+ */
+ while ((ceu_read(pcdev, CSTSR) & 1) && time_before(jiffies, timeout))
+ msleep(1);
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(pcdev->ici.v4l2_dev.dev,
+ "Timeout waiting for frame end! Interface problem?\n");
+ return;
+ }
+
+ /* Wait until reset clears, this shall not hang... */
+ while (ceu_read(pcdev, CAPSR) & (1 << 16))
+ udelay(10);
+
+ /* Anything to restore? */
+ if (capsr & ~(1 << 16))
+ ceu_write(pcdev, CAPSR, capsr);
+}
+
static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
__u32 pixfmt)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- int ret, buswidth, width, height, cfszr_width, cdwdr_width;
+ int ret;
unsigned long camera_flags, common_flags, value;
- int yuv_mode, yuv_lineskip;
+ int yuv_lineskip;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ u32 capsr = capture_save_reset(pcdev);
camera_flags = icd->ops->query_bus_param(icd);
common_flags = soc_camera_bus_param_compatible(camera_flags,
@@ -431,10 +598,10 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
switch (common_flags & SOCAM_DATAWIDTH_MASK) {
case SOCAM_DATAWIDTH_8:
- buswidth = 8;
+ pcdev->is_16bit = 0;
break;
case SOCAM_DATAWIDTH_16:
- buswidth = 16;
+ pcdev->is_16bit = 1;
break;
default:
return -EINVAL;
@@ -444,7 +611,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
ceu_write(pcdev, CRCMPR, 0);
value = 0x00000010; /* data fetch by default */
- yuv_mode = yuv_lineskip = 0;
+ yuv_lineskip = 0;
switch (icd->current_fmt->fourcc) {
case V4L2_PIX_FMT_NV12:
@@ -453,8 +620,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
/* fall-through */
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
- yuv_mode = 1;
- switch (pcdev->camera_fmt->fourcc) {
+ switch (cam->camera_fmt->fourcc) {
case V4L2_PIX_FMT_UYVY:
value = 0x00000000; /* Cb0, Y0, Cr0, Y1 */
break;
@@ -478,36 +644,16 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
- value |= buswidth == 16 ? 1 << 12 : 0;
+ value |= pcdev->is_16bit ? 1 << 12 : 0;
ceu_write(pcdev, CAMCR, value);
ceu_write(pcdev, CAPCR, 0x00300000);
ceu_write(pcdev, CAIFR, pcdev->is_interlaced ? 0x101 : 0);
+ sh_mobile_ceu_set_rect(icd, icd->user_width, icd->user_height);
mdelay(1);
- if (yuv_mode) {
- width = icd->width * 2;
- width = buswidth == 16 ? width / 2 : width;
- cfszr_width = cdwdr_width = icd->width;
- } else {
- width = icd->width * ((icd->current_fmt->depth + 7) >> 3);
- width = buswidth == 16 ? width / 2 : width;
- cfszr_width = buswidth == 8 ? width / 2 : width;
- cdwdr_width = buswidth == 16 ? width * 2 : width;
- }
-
- height = icd->height;
- if (pcdev->is_interlaced) {
- height /= 2;
- cdwdr_width *= 2;
- }
-
- ceu_write(pcdev, CAMOR, 0);
- ceu_write(pcdev, CAPWR, (height << 16) | width);
- ceu_write(pcdev, CFLCR, 0); /* no scaling */
- ceu_write(pcdev, CFSZR, (height << 16) | cfszr_width);
- ceu_write(pcdev, CLFCR, 0); /* no lowpass filter */
+ ceu_write(pcdev, CFLCR, pcdev->cflcr);
/* A few words about byte order (observed in Big Endian mode)
*
@@ -526,10 +672,15 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
ceu_write(pcdev, CDOCR, value);
-
- ceu_write(pcdev, CDWDR, cdwdr_width);
ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
+ dev_dbg(icd->dev.parent, "S_FMT successful for %c%c%c%c %ux%u\n",
+ pixfmt & 0xff, (pixfmt >> 8) & 0xff,
+ (pixfmt >> 16) & 0xff, (pixfmt >> 24) & 0xff,
+ icd->user_width, icd->user_height);
+
+ capture_restore(pcdev, capsr);
+
/* not in bundle mode: skip CBDSR, CDAYR2, CDACR2, CDBYR2, CDBCR2 */
return 0;
}
@@ -579,24 +730,35 @@ static const struct soc_camera_data_format sh_mobile_ceu_formats[] = {
static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
struct soc_camera_format_xlate *xlate)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct device *dev = icd->dev.parent;
int ret, k, n;
int formats = 0;
+ struct sh_mobile_ceu_cam *cam;
ret = sh_mobile_ceu_try_bus_param(icd);
if (ret < 0)
return 0;
+ if (!icd->host_priv) {
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+
+ icd->host_priv = cam;
+ } else {
+ cam = icd->host_priv;
+ }
+
/* Beginning of a pass */
if (!idx)
- icd->host_priv = NULL;
+ cam->extra_fmt = NULL;
switch (icd->formats[idx].fourcc) {
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YVYU:
- if (icd->host_priv)
+ if (cam->extra_fmt)
goto add_single_format;
/*
@@ -608,7 +770,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
* the host_priv pointer and check whether the format you're
* going to add now is already there.
*/
- icd->host_priv = (void *)sh_mobile_ceu_formats;
+ cam->extra_fmt = (void *)sh_mobile_ceu_formats;
n = ARRAY_SIZE(sh_mobile_ceu_formats);
formats += n;
@@ -617,7 +779,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = icd->formats[idx].depth;
xlate++;
- dev_dbg(ici->dev, "Providing format %s using %s\n",
+ dev_dbg(dev, "Providing format %s using %s\n",
sh_mobile_ceu_formats[k].name,
icd->formats[idx].name);
}
@@ -630,7 +792,7 @@ add_single_format:
xlate->cam_fmt = icd->formats + idx;
xlate->buswidth = icd->formats[idx].depth;
xlate++;
- dev_dbg(ici->dev,
+ dev_dbg(dev,
"Providing format %s in pass-through mode\n",
icd->formats[idx].name);
}
@@ -639,82 +801,714 @@ add_single_format:
return formats;
}
+static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd)
+{
+ kfree(icd->host_priv);
+ icd->host_priv = NULL;
+}
+
+/* Check if any dimension of r1 is smaller than respective one of r2 */
+static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2)
+{
+ return r1->width < r2->width || r1->height < r2->height;
+}
+
+/* Check if r1 fails to cover r2 */
+static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2)
+{
+ return r1->left > r2->left || r1->top > r2->top ||
+ r1->left + r1->width < r2->left + r2->width ||
+ r1->top + r1->height < r2->top + r2->height;
+}
+
+static unsigned int scale_down(unsigned int size, unsigned int scale)
+{
+ return (size * 4096 + scale / 2) / scale;
+}
+
+static unsigned int scale_up(unsigned int size, unsigned int scale)
+{
+ return (size * scale + 2048) / 4096;
+}
+
+static unsigned int calc_generic_scale(unsigned int input, unsigned int output)
+{
+ return (input * 4096 + output / 2) / output;
+}
+
+static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect)
+{
+ struct v4l2_crop crop;
+ struct v4l2_cropcap cap;
+ int ret;
+
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_crop, &crop);
+ if (!ret) {
+ *rect = crop.c;
+ return ret;
+ }
+
+ /* Camera driver doesn't support .g_crop(), assume default rectangle */
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ *rect = cap.defrect;
+
+ return ret;
+}
+
+/*
+ * The common for both scaling and cropping iterative approach is:
+ * 1. try if the client can produce exactly what requested by the user
+ * 2. if (1) failed, try to double the client image until we get one big enough
+ * 3. if (2) failed, try to request the maximum image
+ */
+static int client_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *crop,
+ struct v4l2_crop *cam_crop)
+{
+ struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c;
+ struct device *dev = sd->v4l2_dev->dev;
+ struct v4l2_cropcap cap;
+ int ret;
+ unsigned int width, height;
+
+ v4l2_subdev_call(sd, video, s_crop, crop);
+ ret = client_g_rect(sd, cam_rect);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Now cam_crop contains the current camera input rectangle, and it must
+ * be within camera cropcap bounds
+ */
+ if (!memcmp(rect, cam_rect, sizeof(*rect))) {
+ /* Even if camera S_CROP failed, but camera rectangle matches */
+ dev_dbg(dev, "Camera S_CROP successful for %ux%u@%u:%u\n",
+ rect->width, rect->height, rect->left, rect->top);
+ return 0;
+ }
+
+ /* Try to fix cropping, that camera hasn't managed to set */
+ dev_geo(dev, "Fix camera S_CROP for %ux%u@%u:%u to %ux%u@%u:%u\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top,
+ rect->width, rect->height, rect->left, rect->top);
+
+ /* We need sensor maximum rectangle */
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2,
+ cap.bounds.width);
+ soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4,
+ cap.bounds.height);
+
+ /*
+ * Popular special case - some cameras can only handle fixed sizes like
+ * QVGA, VGA,... Take care to avoid infinite loop.
+ */
+ width = max(cam_rect->width, 2);
+ height = max(cam_rect->height, 2);
+
+ while (!ret && (is_smaller(cam_rect, rect) ||
+ is_inside(cam_rect, rect)) &&
+ (cap.bounds.width > width || cap.bounds.height > height)) {
+
+ width *= 2;
+ height *= 2;
+
+ cam_rect->width = width;
+ cam_rect->height = height;
+
+ /*
+ * We do not know what capabilities the camera has to set up
+ * left and top borders. We could try to be smarter in iterating
+ * them, e.g., if camera current left is to the right of the
+ * target left, set it to the middle point between the current
+ * left and minimum left. But that would add too much
+ * complexity: we would have to iterate each border separately.
+ */
+ if (cam_rect->left > rect->left)
+ cam_rect->left = cap.bounds.left;
+
+ if (cam_rect->left + cam_rect->width < rect->left + rect->width)
+ cam_rect->width = rect->left + rect->width -
+ cam_rect->left;
+
+ if (cam_rect->top > rect->top)
+ cam_rect->top = cap.bounds.top;
+
+ if (cam_rect->top + cam_rect->height < rect->top + rect->height)
+ cam_rect->height = rect->top + rect->height -
+ cam_rect->top;
+
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for %ux%u@%u:%u\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ /* S_CROP must not modify the rectangle */
+ if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) {
+ /*
+ * The camera failed to configure a suitable cropping,
+ * we cannot use the current rectangle, set to max
+ */
+ *cam_rect = cap.bounds;
+ v4l2_subdev_call(sd, video, s_crop, cam_crop);
+ ret = client_g_rect(sd, cam_rect);
+ dev_geo(dev, "Camera S_CROP %d for max %ux%u@%u:%u\n", ret,
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+ }
+
+ return ret;
+}
+
+static int get_camera_scales(struct v4l2_subdev *sd, struct v4l2_rect *rect,
+ unsigned int *scale_h, unsigned int *scale_v)
+{
+ struct v4l2_format f;
+ int ret;
+
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ if (ret < 0)
+ return ret;
+
+ *scale_h = calc_generic_scale(rect->width, f.fmt.pix.width);
+ *scale_v = calc_generic_scale(rect->height, f.fmt.pix.height);
+
+ return 0;
+}
+
+static int get_camera_subwin(struct soc_camera_device *icd,
+ struct v4l2_rect *cam_subrect,
+ unsigned int cam_hscale, unsigned int cam_vscale)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_rect *ceu_rect = &cam->ceu_rect;
+
+ if (!ceu_rect->width) {
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->dev.parent;
+ struct v4l2_format f;
+ struct v4l2_pix_format *pix = &f.fmt.pix;
+ int ret;
+ /* First time */
+
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, g_fmt, &f);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "camera fmt %ux%u\n", pix->width, pix->height);
+
+ if (pix->width > 2560) {
+ ceu_rect->width = 2560;
+ ceu_rect->left = (pix->width - 2560) / 2;
+ } else {
+ ceu_rect->width = pix->width;
+ ceu_rect->left = 0;
+ }
+
+ if (pix->height > 1920) {
+ ceu_rect->height = 1920;
+ ceu_rect->top = (pix->height - 1920) / 2;
+ } else {
+ ceu_rect->height = pix->height;
+ ceu_rect->top = 0;
+ }
+
+ dev_geo(dev, "initialised CEU rect %ux%u@%u:%u\n",
+ ceu_rect->width, ceu_rect->height,
+ ceu_rect->left, ceu_rect->top);
+ }
+
+ cam_subrect->width = scale_up(ceu_rect->width, cam_hscale);
+ cam_subrect->left = scale_up(ceu_rect->left, cam_hscale);
+ cam_subrect->height = scale_up(ceu_rect->height, cam_vscale);
+ cam_subrect->top = scale_up(ceu_rect->top, cam_vscale);
+
+ return 0;
+}
+
+static int client_s_fmt(struct soc_camera_device *icd, struct v4l2_format *f,
+ bool ceu_can_scale)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->dev.parent;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ unsigned int width = pix->width, height = pix->height, tmp_w, tmp_h;
+ unsigned int max_width, max_height;
+ struct v4l2_cropcap cap;
+ int ret;
+
+ cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = v4l2_subdev_call(sd, video, cropcap, &cap);
+ if (ret < 0)
+ return ret;
+
+ max_width = min(cap.bounds.width, 2560);
+ max_height = min(cap.bounds.height, 1920);
+
+ ret = v4l2_subdev_call(sd, video, s_fmt, f);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "camera scaled to %ux%u\n", pix->width, pix->height);
+
+ if ((width == pix->width && height == pix->height) || !ceu_can_scale)
+ return 0;
+
+ /* Camera set a format, but geometry is not precise, try to improve */
+ tmp_w = pix->width;
+ tmp_h = pix->height;
+
+ /* width <= max_width && height <= max_height - guaranteed by try_fmt */
+ while ((width > tmp_w || height > tmp_h) &&
+ tmp_w < max_width && tmp_h < max_height) {
+ tmp_w = min(2 * tmp_w, max_width);
+ tmp_h = min(2 * tmp_h, max_height);
+ pix->width = tmp_w;
+ pix->height = tmp_h;
+ ret = v4l2_subdev_call(sd, video, s_fmt, f);
+ dev_geo(dev, "Camera scaled to %ux%u\n",
+ pix->width, pix->height);
+ if (ret < 0) {
+ /* This shouldn't happen */
+ dev_err(dev, "Client failed to set format: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * @rect - camera cropped rectangle
+ * @sub_rect - CEU cropped rectangle, mapped back to camera input area
+ * @ceu_rect - on output calculated CEU crop rectangle
+ */
+static int client_scale(struct soc_camera_device *icd, struct v4l2_rect *rect,
+ struct v4l2_rect *sub_rect, struct v4l2_rect *ceu_rect,
+ struct v4l2_format *f, bool ceu_can_scale)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct device *dev = icd->dev.parent;
+ struct v4l2_format f_tmp = *f;
+ struct v4l2_pix_format *pix_tmp = &f_tmp.fmt.pix;
+ unsigned int scale_h, scale_v;
+ int ret;
+
+ /* 5. Apply iterative camera S_FMT for camera user window. */
+ ret = client_s_fmt(icd, &f_tmp, ceu_can_scale);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "5: camera scaled to %ux%u\n",
+ pix_tmp->width, pix_tmp->height);
+
+ /* 6. Retrieve camera output window (g_fmt) */
+
+ /* unneeded - it is already in "f_tmp" */
+
+ /* 7. Calculate new camera scales. */
+ ret = get_camera_scales(sd, rect, &scale_h, &scale_v);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "7: camera scales %u:%u\n", scale_h, scale_v);
+
+ cam->cam_width = pix_tmp->width;
+ cam->cam_height = pix_tmp->height;
+ f->fmt.pix.width = pix_tmp->width;
+ f->fmt.pix.height = pix_tmp->height;
+
+ /*
+ * 8. Calculate new CEU crop - apply camera scales to previously
+ * calculated "effective" crop.
+ */
+ ceu_rect->left = scale_down(sub_rect->left, scale_h);
+ ceu_rect->width = scale_down(sub_rect->width, scale_h);
+ ceu_rect->top = scale_down(sub_rect->top, scale_v);
+ ceu_rect->height = scale_down(sub_rect->height, scale_v);
+
+ dev_geo(dev, "8: new CEU rect %ux%u@%u:%u\n",
+ ceu_rect->width, ceu_rect->height,
+ ceu_rect->left, ceu_rect->top);
+
+ return 0;
+}
+
+/* Get combined scales */
+static int get_scales(struct soc_camera_device *icd,
+ unsigned int *scale_h, unsigned int *scale_v)
+{
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_crop cam_crop;
+ unsigned int width_in, height_in;
+ int ret;
+
+ cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = client_g_rect(sd, &cam_crop.c);
+ if (ret < 0)
+ return ret;
+
+ ret = get_camera_scales(sd, &cam_crop.c, scale_h, scale_v);
+ if (ret < 0)
+ return ret;
+
+ width_in = scale_up(cam->ceu_rect.width, *scale_h);
+ height_in = scale_up(cam->ceu_rect.height, *scale_v);
+
+ *scale_h = calc_generic_scale(cam->ceu_rect.width, icd->user_width);
+ *scale_v = calc_generic_scale(cam->ceu_rect.height, icd->user_height);
+
+ return 0;
+}
+
+/*
+ * CEU can scale and crop, but we don't want to waste bandwidth and kill the
+ * framerate by always requesting the maximum image from the client. See
+ * Documentation/video4linux/sh_mobile_camera_ceu.txt for a description of
+ * scaling and cropping algorithms and for the meaning of referenced here steps.
+ */
static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+ struct v4l2_crop *a)
{
- return icd->ops->set_crop(icd, rect);
+ struct v4l2_rect *rect = &a->c;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ struct v4l2_crop cam_crop;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_rect *cam_rect = &cam_crop.c, *ceu_rect = &cam->ceu_rect;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->dev.parent;
+ struct v4l2_format f;
+ struct v4l2_pix_format *pix = &f.fmt.pix;
+ unsigned int scale_comb_h, scale_comb_v, scale_ceu_h, scale_ceu_v,
+ out_width, out_height;
+ u32 capsr, cflcr;
+ int ret;
+
+ /* 1. Calculate current combined scales. */
+ ret = get_scales(icd, &scale_comb_h, &scale_comb_v);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "1: combined scales %u:%u\n", scale_comb_h, scale_comb_v);
+
+ /* 2. Apply iterative camera S_CROP for new input window. */
+ ret = client_s_crop(sd, a, &cam_crop);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "2: camera cropped to %ux%u@%u:%u\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+
+ /* On success cam_crop contains current camera crop */
+
+ /*
+ * 3. If old combined scales applied to new crop produce an impossible
+ * user window, adjust scales to produce nearest possible window.
+ */
+ out_width = scale_down(rect->width, scale_comb_h);
+ out_height = scale_down(rect->height, scale_comb_v);
+
+ if (out_width > 2560)
+ out_width = 2560;
+ else if (out_width < 2)
+ out_width = 2;
+
+ if (out_height > 1920)
+ out_height = 1920;
+ else if (out_height < 4)
+ out_height = 4;
+
+ dev_geo(dev, "3: Adjusted output %ux%u\n", out_width, out_height);
+
+ /* 4. Use G_CROP to retrieve actual input window: already in cam_crop */
+
+ /*
+ * 5. Using actual input window and calculated combined scales calculate
+ * camera target output window.
+ */
+ pix->width = scale_down(cam_rect->width, scale_comb_h);
+ pix->height = scale_down(cam_rect->height, scale_comb_v);
+
+ dev_geo(dev, "5: camera target %ux%u\n", pix->width, pix->height);
+
+ /* 6. - 9. */
+ pix->pixelformat = cam->camera_fmt->fourcc;
+ pix->colorspace = cam->camera_fmt->colorspace;
+
+ capsr = capture_save_reset(pcdev);
+ dev_dbg(dev, "CAPSR 0x%x, CFLCR 0x%x\n", capsr, pcdev->cflcr);
+
+ /* Make relative to camera rectangle */
+ rect->left -= cam_rect->left;
+ rect->top -= cam_rect->top;
+
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ ret = client_scale(icd, cam_rect, rect, ceu_rect, &f,
+ pcdev->image_mode && !pcdev->is_interlaced);
+
+ dev_geo(dev, "6-9: %d\n", ret);
+
+ /* 10. Use CEU cropping to crop to the new window. */
+ sh_mobile_ceu_set_rect(icd, out_width, out_height);
+
+ dev_geo(dev, "10: CEU cropped to %ux%u@%u:%u\n",
+ ceu_rect->width, ceu_rect->height,
+ ceu_rect->left, ceu_rect->top);
+
+ /*
+ * 11. Calculate CEU scales from camera scales from results of (10) and
+ * user window from (3)
+ */
+ scale_ceu_h = calc_scale(ceu_rect->width, &out_width);
+ scale_ceu_v = calc_scale(ceu_rect->height, &out_height);
+
+ dev_geo(dev, "11: CEU scales %u:%u\n", scale_ceu_h, scale_ceu_v);
+
+ /* 12. Apply CEU scales. */
+ cflcr = scale_ceu_h | (scale_ceu_v << 16);
+ if (cflcr != pcdev->cflcr) {
+ pcdev->cflcr = cflcr;
+ ceu_write(pcdev, CFLCR, cflcr);
+ }
+
+ /* Restore capture */
+ if (pcdev->active)
+ capsr |= 1;
+ capture_restore(pcdev, capsr);
+
+ icd->user_width = out_width;
+ icd->user_height = out_height;
+
+ /* Even if only camera cropping succeeded */
+ return ret;
}
+/* Similar to set_crop multistage iterative algorithm */
static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
struct sh_mobile_ceu_dev *pcdev = ici->priv;
- __u32 pixfmt = f->fmt.pix.pixelformat;
- const struct soc_camera_format_xlate *xlate;
+ struct sh_mobile_ceu_cam *cam = icd->host_priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_format cam_f = *f;
+ struct v4l2_pix_format *cam_pix = &cam_f.fmt.pix;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->dev.parent;
+ __u32 pixfmt = pix->pixelformat;
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_crop cam_crop;
+ struct v4l2_rect *cam_rect = &cam_crop.c, cam_subrect, ceu_rect;
+ unsigned int scale_cam_h, scale_cam_v;
+ u16 scale_v, scale_h;
int ret;
+ bool is_interlaced, image_mode;
+
+ switch (pix->field) {
+ case V4L2_FIELD_INTERLACED:
+ is_interlaced = true;
+ break;
+ case V4L2_FIELD_ANY:
+ default:
+ pix->field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_NONE:
+ is_interlaced = false;
+ break;
+ }
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pixfmt);
+ dev_warn(dev, "Format %x not found\n", pixfmt);
return -EINVAL;
}
- cam_f.fmt.pix.pixelformat = xlate->cam_fmt->fourcc;
- ret = icd->ops->set_fmt(icd, &cam_f);
+ /* 1. Calculate current camera scales. */
+ cam_crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- if (!ret) {
- icd->buswidth = xlate->buswidth;
- icd->current_fmt = xlate->host_fmt;
- pcdev->camera_fmt = xlate->cam_fmt;
+ ret = client_g_rect(sd, cam_rect);
+ if (ret < 0)
+ return ret;
+
+ ret = get_camera_scales(sd, cam_rect, &scale_cam_h, &scale_cam_v);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "1: camera scales %u:%u\n", scale_cam_h, scale_cam_v);
+
+ /*
+ * 2. Calculate "effective" input crop (sensor subwindow) - CEU crop
+ * scaled back at current camera scales onto input window.
+ */
+ ret = get_camera_subwin(icd, &cam_subrect, scale_cam_h, scale_cam_v);
+ if (ret < 0)
+ return ret;
+
+ dev_geo(dev, "2: subwin %ux%u@%u:%u\n",
+ cam_subrect.width, cam_subrect.height,
+ cam_subrect.left, cam_subrect.top);
+
+ /*
+ * 3. Calculate new combined scales from "effective" input window to
+ * requested user window.
+ */
+ scale_h = calc_generic_scale(cam_subrect.width, pix->width);
+ scale_v = calc_generic_scale(cam_subrect.height, pix->height);
+
+ dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v);
+
+ /*
+ * 4. Calculate camera output window by applying combined scales to real
+ * input window.
+ */
+ cam_pix->width = scale_down(cam_rect->width, scale_h);
+ cam_pix->height = scale_down(cam_rect->height, scale_v);
+ cam_pix->pixelformat = xlate->cam_fmt->fourcc;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ image_mode = true;
+ break;
+ default:
+ image_mode = false;
}
- return ret;
+ dev_geo(dev, "4: camera output %ux%u\n",
+ cam_pix->width, cam_pix->height);
+
+ /* 5. - 9. */
+ ret = client_scale(icd, cam_rect, &cam_subrect, &ceu_rect, &cam_f,
+ image_mode && !is_interlaced);
+
+ dev_geo(dev, "5-9: client scale %d\n", ret);
+
+ /* Done with the camera. Now see if we can improve the result */
+
+ dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n",
+ ret, cam_pix->width, cam_pix->height, pix->width, pix->height);
+ if (ret < 0)
+ return ret;
+
+ /* 10. Use CEU scaling to scale to the requested user window. */
+
+ /* We cannot scale up */
+ if (pix->width > cam_pix->width)
+ pix->width = cam_pix->width;
+ if (pix->width > ceu_rect.width)
+ pix->width = ceu_rect.width;
+
+ if (pix->height > cam_pix->height)
+ pix->height = cam_pix->height;
+ if (pix->height > ceu_rect.height)
+ pix->height = ceu_rect.height;
+
+ /* Let's rock: scale pix->{width x height} down to width x height */
+ scale_h = calc_scale(ceu_rect.width, &pix->width);
+ scale_v = calc_scale(ceu_rect.height, &pix->height);
+
+ dev_geo(dev, "10: W: %u : 0x%x = %u, H: %u : 0x%x = %u\n",
+ ceu_rect.width, scale_h, pix->width,
+ ceu_rect.height, scale_v, pix->height);
+
+ pcdev->cflcr = scale_h | (scale_v << 16);
+
+ icd->buswidth = xlate->buswidth;
+ icd->current_fmt = xlate->host_fmt;
+ cam->camera_fmt = xlate->cam_fmt;
+ cam->ceu_rect = ceu_rect;
+
+ pcdev->is_interlaced = is_interlaced;
+ pcdev->image_mode = image_mode;
+
+ return 0;
}
static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct sh_mobile_ceu_dev *pcdev = ici->priv;
const struct soc_camera_format_xlate *xlate;
- __u32 pixfmt = f->fmt.pix.pixelformat;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ __u32 pixfmt = pix->pixelformat;
+ int width, height;
int ret;
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
- dev_warn(ici->dev, "Format %x not found\n", pixfmt);
+ dev_warn(icd->dev.parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
/* FIXME: calculate using depth and bus width */
- v4l_bound_align_image(&f->fmt.pix.width, 2, 2560, 1,
- &f->fmt.pix.height, 4, 1920, 2, 0);
+ v4l_bound_align_image(&pix->width, 2, 2560, 1,
+ &pix->height, 4, 1920, 2, 0);
+
+ width = pix->width;
+ height = pix->height;
- f->fmt.pix.bytesperline = f->fmt.pix.width *
+ pix->bytesperline = pix->width *
DIV_ROUND_UP(xlate->host_fmt->depth, 8);
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ pix->sizeimage = pix->height * pix->bytesperline;
+
+ pix->pixelformat = xlate->cam_fmt->fourcc;
/* limit to sensor capabilities */
- ret = icd->ops->try_fmt(icd, f);
+ ret = v4l2_subdev_call(sd, video, try_fmt, f);
+ pix->pixelformat = pixfmt;
if (ret < 0)
return ret;
- switch (f->fmt.pix.field) {
- case V4L2_FIELD_INTERLACED:
- pcdev->is_interlaced = 1;
- break;
- case V4L2_FIELD_ANY:
- f->fmt.pix.field = V4L2_FIELD_NONE;
- /* fall-through */
- case V4L2_FIELD_NONE:
- pcdev->is_interlaced = 0;
- break;
- default:
- ret = -EINVAL;
- break;
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ /* FIXME: check against rect_max after converting soc-camera */
+ /* We can scale precisely, need a bigger image from camera */
+ if (pix->width < width || pix->height < height) {
+ int tmp_w = pix->width, tmp_h = pix->height;
+ pix->width = 2560;
+ pix->height = 1920;
+ ret = v4l2_subdev_call(sd, video, try_fmt, f);
+ if (ret < 0) {
+ /* Shouldn't actually happen... */
+ dev_err(icd->dev.parent,
+ "FIXME: try_fmt() returned %d\n", ret);
+ pix->width = tmp_w;
+ pix->height = tmp_h;
+ }
+ }
+ if (pix->width > width)
+ pix->width = width;
+ if (pix->height > height)
+ pix->height = height;
}
return ret;
@@ -774,7 +1568,7 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
videobuf_queue_dma_contig_init(q,
&sh_mobile_ceu_videobuf_ops,
- ici->dev, &pcdev->lock,
+ icd->dev.parent, &pcdev->lock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
pcdev->is_interlaced ?
V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE,
@@ -782,22 +1576,76 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
icd);
}
+static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd,
+ struct v4l2_control *ctrl)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+ u32 val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_SHARPNESS:
+ val = ceu_read(pcdev, CLFCR);
+ ctrl->value = val ^ 1;
+ return 0;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd,
+ struct v4l2_control *ctrl)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
+
+ switch (ctrl->id) {
+ case V4L2_CID_SHARPNESS:
+ switch (icd->current_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ ceu_write(pcdev, CLFCR, !ctrl->value);
+ return 0;
+ }
+ return -EINVAL;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Low-pass filter",
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+};
+
static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
.owner = THIS_MODULE,
.add = sh_mobile_ceu_add_device,
.remove = sh_mobile_ceu_remove_device,
.get_formats = sh_mobile_ceu_get_formats,
+ .put_formats = sh_mobile_ceu_put_formats,
.set_crop = sh_mobile_ceu_set_crop,
.set_fmt = sh_mobile_ceu_set_fmt,
.try_fmt = sh_mobile_ceu_try_fmt,
+ .set_ctrl = sh_mobile_ceu_set_ctrl,
+ .get_ctrl = sh_mobile_ceu_get_ctrl,
.reqbufs = sh_mobile_ceu_reqbufs,
.poll = sh_mobile_ceu_poll,
.querycap = sh_mobile_ceu_querycap,
.set_bus_param = sh_mobile_ceu_set_bus_param,
.init_videobuf = sh_mobile_ceu_init_videobuf,
+ .controls = sh_mobile_ceu_controls,
+ .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls),
};
-static int sh_mobile_ceu_probe(struct platform_device *pdev)
+static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
{
struct sh_mobile_ceu_dev *pcdev;
struct resource *res;
@@ -875,7 +1723,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
}
pcdev->ici.priv = pcdev;
- pcdev->ici.dev = &pdev->dev;
+ pcdev->ici.v4l2_dev.dev = &pdev->dev;
pcdev->ici.nr = pdev->id;
pcdev->ici.drv_name = dev_name(&pdev->dev);
pcdev->ici.ops = &sh_mobile_ceu_host_ops;
@@ -901,7 +1749,7 @@ exit:
return err;
}
-static int sh_mobile_ceu_remove(struct platform_device *pdev)
+static int __devexit sh_mobile_ceu_remove(struct platform_device *pdev)
{
struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
struct sh_mobile_ceu_dev *pcdev = container_of(soc_host,
@@ -922,7 +1770,7 @@ static struct platform_driver sh_mobile_ceu_driver = {
.name = "sh_mobile_ceu",
},
.probe = sh_mobile_ceu_probe,
- .remove = sh_mobile_ceu_remove,
+ .remove = __exit_p(sh_mobile_ceu_remove),
};
static int __init sh_mobile_ceu_init(void)
@@ -941,3 +1789,4 @@ module_exit(sh_mobile_ceu_exit);
MODULE_DESCRIPTION("SuperH Mobile CEU driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sh_mobile_ceu");
diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c
index 4f315c1b7..55d4d9a22 100644
--- a/linux/drivers/media/video/soc_camera.c
+++ b/linux/drivers/media/video/soc_camera.c
@@ -21,15 +21,15 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/list.h>
-#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/vmalloc.h>
#include <media/soc_camera.h>
#include <media/v4l2-common.h>
-#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
#include <media/videobuf-core.h>
#include "compat.h"
@@ -39,7 +39,7 @@
static LIST_HEAD(hosts);
static LIST_HEAD(devices);
-static DEFINE_MUTEX(list_lock);
+static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */
const struct soc_camera_data_format *soc_camera_format_by_fourcc(
struct soc_camera_device *icd, unsigned int fourcc)
@@ -153,12 +153,9 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- int ret = 0;
-
- if (icd->ops->set_std)
- ret = icd->ops->set_std(icd, a);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- return ret;
+ return v4l2_subdev_call(sd, core, s_std, *a);
}
static int soc_camera_reqbufs(struct file *file, void *priv,
@@ -171,8 +168,6 @@ static int soc_camera_reqbufs(struct file *file, void *priv,
WARN_ON(priv != file->private_data);
- dev_dbg(&icd->dev, "%s: %d\n", __func__, p->memory);
-
ret = videobuf_reqbufs(&icf->vb_vidq, p);
if (ret < 0)
return ret;
@@ -210,10 +205,11 @@ static int soc_camera_dqbuf(struct file *file, void *priv,
return videobuf_dqbuf(&icf->vb_vidq, p, file->f_flags & O_NONBLOCK);
}
+/* Always entered with .video_lock held */
static int soc_camera_init_user_formats(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- int i, fmts = 0;
+ int i, fmts = 0, ret;
if (!ici->ops->get_formats)
/*
@@ -226,8 +222,12 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
* First pass - only count formats this host-sensor
* configuration can provide
*/
- for (i = 0; i < icd->num_formats; i++)
- fmts += ici->ops->get_formats(icd, i, NULL);
+ for (i = 0; i < icd->num_formats; i++) {
+ ret = ici->ops->get_formats(icd, i, NULL);
+ if (ret < 0)
+ return ret;
+ fmts += ret;
+ }
if (!fmts)
return -ENXIO;
@@ -249,20 +249,39 @@ static int soc_camera_init_user_formats(struct soc_camera_device *icd)
icd->user_formats[i].cam_fmt = icd->formats + i;
icd->user_formats[i].buswidth = icd->formats[i].depth;
} else {
- fmts += ici->ops->get_formats(icd, i,
- &icd->user_formats[fmts]);
+ ret = ici->ops->get_formats(icd, i,
+ &icd->user_formats[fmts]);
+ if (ret < 0)
+ goto egfmt;
+ fmts += ret;
}
icd->current_fmt = icd->user_formats[0].host_fmt;
return 0;
+
+egfmt:
+ icd->num_user_formats = 0;
+ vfree(icd->user_formats);
+ return ret;
}
+/* Always entered with .video_lock held */
static void soc_camera_free_user_formats(struct soc_camera_device *icd)
{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+
+ if (ici->ops->put_formats)
+ ici->ops->put_formats(icd);
+ icd->current_fmt = NULL;
+ icd->num_user_formats = 0;
vfree(icd->user_formats);
+ icd->user_formats = NULL;
}
+#define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
+ ((x) >> 24) & 0xff
+
/* Called with .vb_lock held */
static int soc_camera_set_fmt(struct soc_camera_file *icf,
struct v4l2_format *f)
@@ -272,6 +291,9 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
struct v4l2_pix_format *pix = &f->fmt.pix;
int ret;
+ dev_dbg(&icd->dev, "S_FMT(%c%c%c%c, %ux%u)\n",
+ pixfmtstr(pix->pixelformat), pix->width, pix->height);
+
/* We always call try_fmt() before set_fmt() or set_crop() */
ret = ici->ops->try_fmt(icd, f);
if (ret < 0)
@@ -282,13 +304,13 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
return ret;
} else if (!icd->current_fmt ||
icd->current_fmt->fourcc != pix->pixelformat) {
- dev_err(ici->dev,
+ dev_err(&icd->dev,
"Host driver hasn't set up current format correctly!\n");
return -EINVAL;
}
- icd->width = pix->width;
- icd->height = pix->height;
+ icd->user_width = pix->width;
+ icd->user_height = pix->height;
icf->vb_vidq.field =
icd->field = pix->field;
@@ -297,7 +319,7 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
f->type);
dev_dbg(&icd->dev, "set width: %d height: %d\n",
- icd->width, icd->height);
+ icd->user_width, icd->user_height);
/* set physical bus parameters */
return ici->ops->set_bus_param(icd, pix->pixelformat);
@@ -305,30 +327,24 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf,
static int soc_camera_open(struct file *file)
{
- struct video_device *vdev;
- struct soc_camera_device *icd;
+ struct video_device *vdev = video_devdata(file);
+ struct soc_camera_device *icd = container_of(vdev->parent,
+ struct soc_camera_device,
+ dev);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
struct soc_camera_host *ici;
struct soc_camera_file *icf;
int ret;
- icf = vmalloc(sizeof(*icf));
- if (!icf)
- return -ENOMEM;
-
- /*
- * It is safe to dereference these pointers now as long as a user has
- * the video device open - we are protected by the held cdev reference.
- */
+ if (!icd->ops)
+ /* No device driver attached */
+ return -ENODEV;
- vdev = video_devdata(file);
- icd = container_of(vdev->parent, struct soc_camera_device, dev);
ici = to_soc_camera_host(icd->dev.parent);
- if (!try_module_get(icd->ops->owner)) {
- dev_err(&icd->dev, "Couldn't lock sensor driver.\n");
- ret = -EINVAL;
- goto emgd;
- }
+ icf = vmalloc(sizeof(*icf));
+ if (!icf)
+ return -ENOMEM;
if (!try_module_get(ici->ops->owner)) {
dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
@@ -336,7 +352,10 @@ static int soc_camera_open(struct file *file)
goto emgi;
}
- /* Protect against icd->remove() until we module_get() both drivers. */
+ /*
+ * Protect against icd->ops->remove() until we module_get() both
+ * drivers.
+ */
mutex_lock(&icd->video_lock);
icf->icd = icd;
@@ -348,14 +367,24 @@ static int soc_camera_open(struct file *file)
struct v4l2_format f = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.fmt.pix = {
- .width = icd->width,
- .height = icd->height,
+ .width = icd->user_width,
+ .height = icd->user_height,
.field = icd->field,
.pixelformat = icd->current_fmt->fourcc,
.colorspace = icd->current_fmt->colorspace,
},
};
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 1);
+ if (ret < 0)
+ goto epower;
+ }
+
+ /* The camera could have been already on, try to reset */
+ if (icl->reset)
+ icl->reset(icd->pdev);
+
ret = ici->ops->add(icd);
if (ret < 0) {
dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
@@ -368,28 +397,29 @@ static int soc_camera_open(struct file *file)
goto esfmt;
}
- mutex_unlock(&icd->video_lock);
-
file->private_data = icf;
dev_dbg(&icd->dev, "camera device open\n");
ici->ops->init_videobuf(&icf->vb_vidq, icd);
+ mutex_unlock(&icd->video_lock);
+
return 0;
/*
- * First three errors are entered with the .video_lock held
+ * First five errors are entered with the .video_lock held
* and use_count == 1
*/
esfmt:
ici->ops->remove(icd);
eiciadd:
+ if (icl->power)
+ icl->power(icd->pdev, 0);
+epower:
icd->use_count--;
mutex_unlock(&icd->video_lock);
module_put(ici->ops->owner);
emgi:
- module_put(icd->ops->owner);
-emgd:
vfree(icf);
return ret;
}
@@ -399,21 +429,24 @@ static int soc_camera_close(struct file *file)
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- struct video_device *vdev = icd->vdev;
mutex_lock(&icd->video_lock);
icd->use_count--;
- if (!icd->use_count)
+ if (!icd->use_count) {
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+
ici->ops->remove(icd);
+ if (icl->power)
+ icl->power(icd->pdev, 0);
+ }
mutex_unlock(&icd->video_lock);
- module_put(icd->ops->owner);
module_put(ici->ops->owner);
vfree(icf);
- dev_dbg(vdev->parent, "camera device close\n");
+ dev_dbg(&icd->dev, "camera device close\n");
return 0;
}
@@ -423,10 +456,9 @@ static ssize_t soc_camera_read(struct file *file, char __user *buf,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
- struct video_device *vdev = icd->vdev;
int err = -EINVAL;
- dev_err(vdev->parent, "camera device read not implemented\n");
+ dev_err(&icd->dev, "camera device read not implemented\n");
return err;
}
@@ -484,8 +516,8 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
mutex_lock(&icf->vb_vidq.vb_lock);
- if (videobuf_queue_is_busy(&icf->vb_vidq)) {
- dev_err(&icd->dev, "S_FMT denied: queue busy\n");
+ if (icf->vb_vidq.bufs[0]) {
+ dev_err(&icd->dev, "S_FMT denied: queue initialised\n");
ret = -EBUSY;
goto unlock;
}
@@ -526,8 +558,8 @@ static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
WARN_ON(priv != file->private_data);
- pix->width = icd->width;
- pix->height = icd->height;
+ pix->width = icd->user_width;
+ pix->height = icd->user_height;
pix->field = icf->vb_vidq.field;
pix->pixelformat = icd->current_fmt->fourcc;
pix->bytesperline = pix->width *
@@ -556,18 +588,17 @@ static int soc_camera_streamon(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
int ret;
WARN_ON(priv != file->private_data);
- dev_dbg(&icd->dev, "%s\n", __func__);
-
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
mutex_lock(&icd->video_lock);
- icd->ops->start_capture(icd);
+ v4l2_subdev_call(sd, video, s_stream, 1);
/* This calls buf_queue from host driver's videobuf_queue_ops */
ret = videobuf_streamon(&icf->vb_vidq);
@@ -582,11 +613,10 @@ static int soc_camera_streamoff(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
WARN_ON(priv != file->private_data);
- dev_dbg(&icd->dev, "%s\n", __func__);
-
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -596,7 +626,7 @@ static int soc_camera_streamoff(struct file *file, void *priv,
* remaining buffers. When the last buffer is freed, stop capture */
videobuf_streamoff(&icf->vb_vidq);
- icd->ops->stop_capture(icd);
+ v4l2_subdev_call(sd, video, s_stream, 0);
mutex_unlock(&icd->video_lock);
@@ -608,6 +638,7 @@ static int soc_camera_queryctrl(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
int i;
WARN_ON(priv != file->private_data);
@@ -615,6 +646,15 @@ static int soc_camera_queryctrl(struct file *file, void *priv,
if (!qc->id)
return -EINVAL;
+ /* First check host controls */
+ for (i = 0; i < ici->ops->num_controls; i++)
+ if (qc->id == ici->ops->controls[i].id) {
+ memcpy(qc, &(ici->ops->controls[i]),
+ sizeof(*qc));
+ return 0;
+ }
+
+ /* Then device controls */
for (i = 0; i < icd->ops->num_controls; i++)
if (qc->id == icd->ops->controls[i].id) {
memcpy(qc, &(icd->ops->controls[i]),
@@ -630,25 +670,19 @@ static int soc_camera_g_ctrl(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret;
WARN_ON(priv != file->private_data);
- switch (ctrl->id) {
- case V4L2_CID_GAIN:
- if (icd->gain == (unsigned short)~0)
- return -EINVAL;
- ctrl->value = icd->gain;
- return 0;
- case V4L2_CID_EXPOSURE:
- if (icd->exposure == (unsigned short)~0)
- return -EINVAL;
- ctrl->value = icd->exposure;
- return 0;
+ if (ici->ops->get_ctrl) {
+ ret = ici->ops->get_ctrl(icd, ctrl);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
}
- if (icd->ops->get_control)
- return icd->ops->get_control(icd, ctrl);
- return -EINVAL;
+ return v4l2_subdev_call(sd, core, g_ctrl, ctrl);
}
static int soc_camera_s_ctrl(struct file *file, void *priv,
@@ -656,12 +690,19 @@ static int soc_camera_s_ctrl(struct file *file, void *priv,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ int ret;
WARN_ON(priv != file->private_data);
- if (icd->ops->set_control)
- return icd->ops->set_control(icd, ctrl);
- return -EINVAL;
+ if (ici->ops->set_ctrl) {
+ ret = ici->ops->set_ctrl(icd, ctrl);
+ if (ret != -ENOIOCTLCMD)
+ return ret;
+ }
+
+ return v4l2_subdev_call(sd, core, s_ctrl, ctrl);
}
static int soc_camera_cropcap(struct file *file, void *fh,
@@ -669,20 +710,9 @@ static int soc_camera_cropcap(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->bounds.left = icd->x_min;
- a->bounds.top = icd->y_min;
- a->bounds.width = icd->width_max;
- a->bounds.height = icd->height_max;
- a->defrect.left = icd->x_min;
- a->defrect.top = icd->y_min;
- a->defrect.width = DEFAULT_WIDTH;
- a->defrect.height = DEFAULT_HEIGHT;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
+ return ici->ops->cropcap(icd, a);
}
static int soc_camera_g_crop(struct file *file, void *fh,
@@ -690,36 +720,53 @@ static int soc_camera_g_crop(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ int ret;
- a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- a->c.left = icd->x_current;
- a->c.top = icd->y_current;
- a->c.width = icd->width;
- a->c.height = icd->height;
+ mutex_lock(&icf->vb_vidq.vb_lock);
+ ret = ici->ops->get_crop(icd, a);
+ mutex_unlock(&icf->vb_vidq.vb_lock);
- return 0;
+ return ret;
}
+/*
+ * According to the V4L2 API, drivers shall not update the struct v4l2_crop
+ * argument with the actual geometry, instead, the user shall use G_CROP to
+ * retrieve it. However, we expect camera host and client drivers to update
+ * the argument, which we then use internally, but do not return to the user.
+ */
static int soc_camera_s_crop(struct file *file, void *fh,
struct v4l2_crop *a)
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct v4l2_rect *rect = &a->c;
+ struct v4l2_crop current_crop;
int ret;
if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
+ dev_dbg(&icd->dev, "S_CROP(%ux%u@%u:%u)\n",
+ rect->width, rect->height, rect->left, rect->top);
+
/* Cropping is allowed during a running capture, guard consistency */
mutex_lock(&icf->vb_vidq.vb_lock);
- ret = ici->ops->set_crop(icd, &a->c);
- if (!ret) {
- icd->width = a->c.width;
- icd->height = a->c.height;
- icd->x_current = a->c.left;
- icd->y_current = a->c.top;
+ /* If get_crop fails, we'll let host and / or client drivers decide */
+ ret = ici->ops->get_crop(icd, &current_crop);
+
+ /* Prohibit window size change with initialised buffers */
+ if (icf->vb_vidq.bufs[0] && !ret &&
+ (a->c.width != current_crop.c.width ||
+ a->c.height != current_crop.c.height)) {
+ dev_err(&icd->dev,
+ "S_CROP denied: queue initialised and sizes differ\n");
+ ret = -EBUSY;
+ } else {
+ ret = ici->ops->set_crop(icd, a);
}
mutex_unlock(&icf->vb_vidq.vb_lock);
@@ -732,11 +779,9 @@ static int soc_camera_g_chip_ident(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- if (!icd->ops->get_chip_id)
- return -EINVAL;
-
- return icd->ops->get_chip_id(icd, id);
+ return v4l2_subdev_call(sd, core, g_chip_ident, id);
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -745,11 +790,9 @@ static int soc_camera_g_register(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- if (!icd->ops->get_register)
- return -EINVAL;
-
- return icd->ops->get_register(icd, reg);
+ return v4l2_subdev_call(sd, core, g_register, reg);
}
static int soc_camera_s_register(struct file *file, void *fh,
@@ -757,37 +800,12 @@ static int soc_camera_s_register(struct file *file, void *fh,
{
struct soc_camera_file *icf = file->private_data;
struct soc_camera_device *icd = icf->icd;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- if (!icd->ops->set_register)
- return -EINVAL;
-
- return icd->ops->set_register(icd, reg);
+ return v4l2_subdev_call(sd, core, s_register, reg);
}
#endif
-static int device_register_link(struct soc_camera_device *icd)
-{
- int ret = dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum);
-
- if (!ret)
- ret = device_register(&icd->dev);
-
- if (ret < 0) {
- /* Prevent calling device_unregister() */
- icd->dev.parent = NULL;
- dev_err(&icd->dev, "Cannot register device: %d\n", ret);
- /* Even if probe() was unsuccessful for all registered drivers,
- * device_register() returns 0, and we add the link, just to
- * document this camera's control device */
- } else if (icd->control)
- /* Have to sysfs_remove_link() before device_unregister()? */
- if (sysfs_create_link(&icd->dev.kobj, &icd->control->kobj,
- "control"))
- dev_warn(&icd->dev,
- "Failed creating the control symlink\n");
- return ret;
-}
-
/* So far this function cannot fail */
static void scan_add_host(struct soc_camera_host *ici)
{
@@ -797,106 +815,194 @@ static void scan_add_host(struct soc_camera_host *ici)
list_for_each_entry(icd, &devices, list) {
if (icd->iface == ici->nr) {
- icd->dev.parent = ici->dev;
- device_register_link(icd);
+ int ret;
+ icd->dev.parent = ici->v4l2_dev.dev;
+ dev_set_name(&icd->dev, "%u-%u", icd->iface,
+ icd->devnum);
+ ret = device_register(&icd->dev);
+ if (ret < 0) {
+ icd->dev.parent = NULL;
+ dev_err(&icd->dev,
+ "Cannot register device: %d\n", ret);
+ }
}
}
mutex_unlock(&list_lock);
}
-/* return: 0 if no match found or a match found and
- * device_register() successful, error code otherwise */
-static int scan_add_device(struct soc_camera_device *icd)
+#ifdef CONFIG_I2C_BOARDINFO
+static int soc_camera_init_i2c(struct soc_camera_device *icd,
+ struct soc_camera_link *icl)
{
- struct soc_camera_host *ici;
- int ret = 0;
+ struct i2c_client *client;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id);
+ struct v4l2_subdev *subdev;
+ int ret;
- mutex_lock(&list_lock);
+ if (!adap) {
+ ret = -ENODEV;
+ dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n",
+ icl->i2c_adapter_id);
+ goto ei2cga;
+ }
- list_add_tail(&icd->list, &devices);
+ icl->board_info->platform_data = icd;
- /* Watch out for class_for_each_device / class_find_device API by
- * Dave Young <hidave.darkstar@gmail.com> */
- list_for_each_entry(ici, &hosts, list) {
- if (icd->iface == ici->nr) {
- ret = 1;
- icd->dev.parent = ici->dev;
- break;
- }
+ subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap,
+ icl->module_name, icl->board_info, NULL);
+ if (!subdev) {
+ ret = -ENOMEM;
+ goto ei2cnd;
}
- mutex_unlock(&list_lock);
+ subdev->grp_id = (__u32)icd;
+ client = subdev->priv;
- if (ret)
- ret = device_register_link(icd);
+ /* Use to_i2c_client(dev) to recover the i2c client */
+ dev_set_drvdata(&icd->dev, &client->dev);
+ return 0;
+ei2cnd:
+ i2c_put_adapter(adap);
+ei2cga:
return ret;
}
+static void soc_camera_free_i2c(struct soc_camera_device *icd)
+{
+ struct i2c_client *client =
+ to_i2c_client(to_soc_camera_control(icd));
+ dev_set_drvdata(&icd->dev, NULL);
+ v4l2_device_unregister_subdev(i2c_get_clientdata(client));
+ i2c_unregister_device(client);
+ i2c_put_adapter(client->adapter);
+}
+#else
+#define soc_camera_init_i2c(icd, icl) (-ENODEV)
+#define soc_camera_free_i2c(icd) do {} while (0)
+#endif
+
+static int soc_camera_video_start(struct soc_camera_device *icd);
+static int video_dev_create(struct soc_camera_device *icd);
+/* Called during host-driver probe */
static int soc_camera_probe(struct device *dev)
{
struct soc_camera_device *icd = to_soc_camera_dev(dev);
- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct device *control = NULL;
+ struct v4l2_subdev *sd;
+ struct v4l2_format f = {.type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
int ret;
- /*
- * Possible race scenario:
- * modprobe <camera-host-driver> triggers __func__
- * at this moment respective <camera-sensor-driver> gets rmmod'ed
- * to protect take module references.
- */
+ dev_info(dev, "Probing %s\n", dev_name(dev));
- if (!try_module_get(icd->ops->owner)) {
- dev_err(&icd->dev, "Couldn't lock sensor driver.\n");
- ret = -EINVAL;
- goto emgd;
+ if (icl->power) {
+ ret = icl->power(icd->pdev, 1);
+ if (ret < 0) {
+ dev_err(dev,
+ "Platform failed to power-on the camera.\n");
+ goto epower;
+ }
}
- if (!try_module_get(ici->ops->owner)) {
- dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
+ /* The camera could have been already on, try to reset */
+ if (icl->reset)
+ icl->reset(icd->pdev);
+
+ ret = ici->ops->add(icd);
+ if (ret < 0)
+ goto eadd;
+
+ /* Must have icd->vdev before registering the device */
+ ret = video_dev_create(icd);
+ if (ret < 0)
+ goto evdc;
+
+ /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
+ if (icl->board_info) {
+ ret = soc_camera_init_i2c(icd, icl);
+ if (ret < 0)
+ goto eadddev;
+ } else if (!icl->add_device || !icl->del_device) {
ret = -EINVAL;
- goto emgi;
+ goto eadddev;
+ } else {
+ if (icl->module_name)
+ ret = request_module(icl->module_name);
+
+ ret = icl->add_device(icl, &icd->dev);
+ if (ret < 0)
+ goto eadddev;
+
+ /*
+ * FIXME: this is racy, have to use driver-binding notification,
+ * when it is available
+ */
+ control = to_soc_camera_control(icd);
+ if (!control || !control->driver || !dev_get_drvdata(control) ||
+ !try_module_get(control->driver->owner)) {
+ icl->del_device(icl);
+ goto enodrv;
+ }
}
+ /* At this point client .probe() should have run already */
+ ret = soc_camera_init_user_formats(icd);
+ if (ret < 0)
+ goto eiufmt;
+
+ icd->field = V4L2_FIELD_ANY;
+
+ /* ..._video_start() will create a device node, so we have to protect */
mutex_lock(&icd->video_lock);
- /* We only call ->add() here to activate and probe the camera.
- * We shall ->remove() and deactivate it immediately afterwards. */
- ret = ici->ops->add(icd);
+ ret = soc_camera_video_start(icd);
if (ret < 0)
- goto eiadd;
+ goto evidstart;
+
+ /* Try to improve our guess of a reasonable window format */
+ sd = soc_camera_to_subdev(icd);
+ if (!v4l2_subdev_call(sd, video, g_fmt, &f)) {
+ icd->user_width = f.fmt.pix.width;
+ icd->user_height = f.fmt.pix.height;
+ }
- ret = icd->ops->probe(icd);
- if (ret >= 0) {
- const struct v4l2_queryctrl *qctrl;
+ /* Do we have to sysfs_remove_link() before device_unregister()? */
+ if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
+ "control"))
+ dev_warn(&icd->dev, "Failed creating the control symlink\n");
- qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN);
- icd->gain = qctrl ? qctrl->default_value : (unsigned short)~0;
- qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
- icd->exposure = qctrl ? qctrl->default_value :
- (unsigned short)~0;
+ ici->ops->remove(icd);
- ret = soc_camera_init_user_formats(icd);
- if (ret < 0) {
- if (icd->ops->remove)
- icd->ops->remove(icd);
- goto eiufmt;
- }
+ if (icl->power)
+ icl->power(icd->pdev, 0);
- icd->height = DEFAULT_HEIGHT;
- icd->width = DEFAULT_WIDTH;
- icd->field = V4L2_FIELD_ANY;
- }
+ mutex_unlock(&icd->video_lock);
+ return 0;
+
+evidstart:
+ mutex_unlock(&icd->video_lock);
+ soc_camera_free_user_formats(icd);
eiufmt:
+ if (icl->board_info) {
+ soc_camera_free_i2c(icd);
+ } else {
+ icl->del_device(icl);
+ module_put(control->driver->owner);
+ }
+enodrv:
+eadddev:
+ video_device_release(icd->vdev);
+evdc:
ici->ops->remove(icd);
-eiadd:
- mutex_unlock(&icd->video_lock);
- module_put(ici->ops->owner);
-emgi:
- module_put(icd->ops->owner);
-emgd:
+eadd:
+ if (icl->power)
+ icl->power(icd->pdev, 0);
+epower:
return ret;
}
@@ -905,12 +1011,28 @@ emgd:
static int soc_camera_remove(struct device *dev)
{
struct soc_camera_device *icd = to_soc_camera_dev(dev);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
+ struct video_device *vdev = icd->vdev;
- mutex_lock(&icd->video_lock);
- if (icd->ops->remove)
- icd->ops->remove(icd);
- mutex_unlock(&icd->video_lock);
+ BUG_ON(!dev->parent);
+ if (vdev) {
+ mutex_lock(&icd->video_lock);
+ video_unregister_device(vdev);
+ icd->vdev = NULL;
+ mutex_unlock(&icd->video_lock);
+ }
+
+ if (icl->board_info) {
+ soc_camera_free_i2c(icd);
+ } else {
+ struct device_driver *drv = to_soc_camera_control(icd) ?
+ to_soc_camera_control(icd)->driver : NULL;
+ if (drv) {
+ icl->del_device(icl);
+ module_put(drv->owner);
+ }
+ }
soc_camera_free_user_formats(icd);
return 0;
@@ -958,14 +1080,33 @@ static void dummy_release(struct device *dev)
{
}
+static int default_cropcap(struct soc_camera_device *icd,
+ struct v4l2_cropcap *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, cropcap, a);
+}
+
+static int default_g_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, g_crop, a);
+}
+
+static int default_s_crop(struct soc_camera_device *icd, struct v4l2_crop *a)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ return v4l2_subdev_call(sd, video, s_crop, a);
+}
+
int soc_camera_host_register(struct soc_camera_host *ici)
{
struct soc_camera_host *ix;
+ int ret;
if (!ici || !ici->ops ||
!ici->ops->try_fmt ||
!ici->ops->set_fmt ||
- !ici->ops->set_crop ||
!ici->ops->set_bus_param ||
!ici->ops->querycap ||
!ici->ops->init_videobuf ||
@@ -973,18 +1114,27 @@ int soc_camera_host_register(struct soc_camera_host *ici)
!ici->ops->add ||
!ici->ops->remove ||
!ici->ops->poll ||
- !ici->dev)
+ !ici->v4l2_dev.dev)
return -EINVAL;
+ if (!ici->ops->set_crop)
+ ici->ops->set_crop = default_s_crop;
+ if (!ici->ops->get_crop)
+ ici->ops->get_crop = default_g_crop;
+ if (!ici->ops->cropcap)
+ ici->ops->cropcap = default_cropcap;
+
mutex_lock(&list_lock);
list_for_each_entry(ix, &hosts, list) {
if (ix->nr == ici->nr) {
- mutex_unlock(&list_lock);
- return -EBUSY;
+ ret = -EBUSY;
+ goto edevreg;
}
}
- dev_set_drvdata(ici->dev, ici);
+ ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
+ if (ret < 0)
+ goto edevreg;
list_add_tail(&ici->list, &hosts);
mutex_unlock(&list_lock);
@@ -992,6 +1142,10 @@ int soc_camera_host_register(struct soc_camera_host *ici)
scan_add_host(ici);
return 0;
+
+edevreg:
+ mutex_unlock(&list_lock);
+ return ret;
}
EXPORT_SYMBOL(soc_camera_host_register);
@@ -1005,42 +1159,34 @@ void soc_camera_host_unregister(struct soc_camera_host *ici)
list_del(&ici->list);
list_for_each_entry(icd, &devices, list) {
- if (icd->dev.parent == ici->dev) {
+ if (icd->iface == ici->nr) {
+ /* The bus->remove will be called */
device_unregister(&icd->dev);
/* Not before device_unregister(), .remove
* needs parent to call ici->ops->remove() */
icd->dev.parent = NULL;
+
+ /* If the host module is loaded again, device_register()
+ * would complain "already initialised" */
memset(&icd->dev.kobj, 0, sizeof(icd->dev.kobj));
}
}
mutex_unlock(&list_lock);
- dev_set_drvdata(ici->dev, NULL);
+ v4l2_device_unregister(&ici->v4l2_dev);
}
EXPORT_SYMBOL(soc_camera_host_unregister);
/* Image capture device */
-int soc_camera_device_register(struct soc_camera_device *icd)
+static int soc_camera_device_register(struct soc_camera_device *icd)
{
struct soc_camera_device *ix;
int num = -1, i;
- if (!icd || !icd->ops ||
- !icd->ops->probe ||
- !icd->ops->init ||
- !icd->ops->release ||
- !icd->ops->start_capture ||
- !icd->ops->stop_capture ||
- !icd->ops->set_crop ||
- !icd->ops->set_fmt ||
- !icd->ops->try_fmt ||
- !icd->ops->query_bus_param ||
- !icd->ops->set_bus_param)
- return -EINVAL;
-
for (i = 0; i < 256 && num < 0; i++) {
num = i;
+ /* Check if this index is available on this interface */
list_for_each_entry(ix, &devices, list) {
if (ix->iface == icd->iface && ix->devnum == i) {
num = -1;
@@ -1062,21 +1208,15 @@ int soc_camera_device_register(struct soc_camera_device *icd)
icd->host_priv = NULL;
mutex_init(&icd->video_lock);
- return scan_add_device(icd);
+ list_add_tail(&icd->list, &devices);
+
+ return 0;
}
-EXPORT_SYMBOL(soc_camera_device_register);
-void soc_camera_device_unregister(struct soc_camera_device *icd)
+static void soc_camera_device_unregister(struct soc_camera_device *icd)
{
- mutex_lock(&list_lock);
list_del(&icd->list);
-
- /* The bus->remove will be eventually called */
- if (icd->dev.parent)
- device_unregister(&icd->dev);
- mutex_unlock(&list_lock);
}
-EXPORT_SYMBOL(soc_camera_device_unregister);
static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
.vidioc_querycap = soc_camera_querycap,
@@ -1107,23 +1247,13 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
#endif
};
-/*
- * Usually called from the struct soc_camera_ops .probe() method, i.e., from
- * soc_camera_probe() above with .video_lock held
- */
-int soc_camera_video_start(struct soc_camera_device *icd)
+static int video_dev_create(struct soc_camera_device *icd)
{
struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- int err = -ENOMEM;
- struct video_device *vdev;
+ struct video_device *vdev = video_device_alloc();
- if (!icd->dev.parent)
- return -ENODEV;
-
- vdev = video_device_alloc();
if (!vdev)
- goto evidallocd;
- dev_dbg(ici->dev, "Allocated video_device %p\n", vdev);
+ return -ENOMEM;
strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
@@ -1133,87 +1263,93 @@ int soc_camera_video_start(struct soc_camera_device *icd)
vdev->ioctl_ops = &soc_camera_ioctl_ops;
vdev->release = video_device_release;
vdev->minor = -1;
- vdev->tvnorms = V4L2_STD_UNKNOWN,
+ vdev->tvnorms = V4L2_STD_UNKNOWN;
- err = video_register_device(vdev, VFL_TYPE_GRABBER, vdev->minor);
- if (err < 0) {
- dev_err(vdev->parent, "video_register_device failed\n");
- goto evidregd;
- }
icd->vdev = vdev;
return 0;
-
-evidregd:
- video_device_release(vdev);
-evidallocd:
- return err;
}
-EXPORT_SYMBOL(soc_camera_video_start);
-/* Called from client .remove() methods with .video_lock held */
-void soc_camera_video_stop(struct soc_camera_device *icd)
+/*
+ * Called from soc_camera_probe() above (with .video_lock held???)
+ */
+static int soc_camera_video_start(struct soc_camera_device *icd)
{
- struct video_device *vdev = icd->vdev;
+ int ret;
- dev_dbg(&icd->dev, "%s\n", __func__);
+ if (!icd->dev.parent)
+ return -ENODEV;
- if (!icd->dev.parent || !vdev)
- return;
+ if (!icd->ops ||
+ !icd->ops->query_bus_param ||
+ !icd->ops->set_bus_param)
+ return -EINVAL;
+
+ ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER,
+ icd->vdev->minor);
+ if (ret < 0) {
+ dev_err(&icd->dev, "video_register_device failed: %d\n", ret);
+ return ret;
+ }
- video_unregister_device(vdev);
- icd->vdev = NULL;
+ return 0;
}
-EXPORT_SYMBOL(soc_camera_video_stop);
static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
{
struct soc_camera_link *icl = pdev->dev.platform_data;
- struct i2c_adapter *adap;
- struct i2c_client *client;
+ struct soc_camera_device *icd;
+ int ret;
if (!icl)
return -EINVAL;
- adap = i2c_get_adapter(icl->i2c_adapter_id);
- if (!adap) {
- dev_warn(&pdev->dev, "Cannot get adapter #%d. No driver?\n",
- icl->i2c_adapter_id);
- /* -ENODEV and -ENXIO do not produce an error on probe()... */
- return -ENOENT;
- }
-
- icl->board_info->platform_data = icl;
- client = i2c_new_device(adap, icl->board_info);
- if (!client) {
- i2c_put_adapter(adap);
+ icd = kzalloc(sizeof(*icd), GFP_KERNEL);
+ if (!icd)
return -ENOMEM;
- }
- platform_set_drvdata(pdev, client);
+ icd->iface = icl->bus_id;
+ icd->pdev = &pdev->dev;
+ platform_set_drvdata(pdev, icd);
+ icd->dev.platform_data = icl;
+
+ ret = soc_camera_device_register(icd);
+ if (ret < 0)
+ goto escdevreg;
+
+ icd->user_width = DEFAULT_WIDTH;
+ icd->user_height = DEFAULT_HEIGHT;
return 0;
+
+escdevreg:
+ kfree(icd);
+
+ return ret;
}
+/* Only called on rmmod for each platform device, since they are not
+ * hot-pluggable. Now we know, that all our users - hosts and devices have
+ * been unloaded already */
static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev)
{
- struct i2c_client *client = platform_get_drvdata(pdev);
+ struct soc_camera_device *icd = platform_get_drvdata(pdev);
- if (!client)
- return -ENODEV;
+ if (!icd)
+ return -EINVAL;
- i2c_unregister_device(client);
- i2c_put_adapter(client->adapter);
+ soc_camera_device_unregister(icd);
+
+ kfree(icd);
return 0;
}
static struct platform_driver __refdata soc_camera_pdrv = {
- .probe = soc_camera_pdrv_probe,
- .remove = __devexit_p(soc_camera_pdrv_remove),
- .driver = {
- .name = "soc-camera-pdrv",
- .owner = THIS_MODULE,
+ .remove = __devexit_p(soc_camera_pdrv_remove),
+ .driver = {
+ .name = "soc-camera-pdrv",
+ .owner = THIS_MODULE,
},
};
@@ -1226,7 +1362,7 @@ static int __init soc_camera_init(void)
if (ret)
goto edrvr;
- ret = platform_driver_register(&soc_camera_pdrv);
+ ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
if (ret)
goto epdr;
diff --git a/linux/drivers/media/video/soc_camera_platform.c b/linux/drivers/media/video/soc_camera_platform.c
index c48676356..1b6dd02a8 100644
--- a/linux/drivers/media/video/soc_camera_platform.c
+++ b/linux/drivers/media/video/soc_camera_platform.c
@@ -16,54 +16,32 @@
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
#include <media/soc_camera.h>
#include <media/soc_camera_platform.h>
struct soc_camera_platform_priv {
- struct soc_camera_platform_info *info;
- struct soc_camera_device icd;
+ struct v4l2_subdev subdev;
struct soc_camera_data_format format;
};
-static struct soc_camera_platform_info *
-soc_camera_platform_get_info(struct soc_camera_device *icd)
+static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev)
{
- struct soc_camera_platform_priv *priv;
- priv = container_of(icd, struct soc_camera_platform_priv, icd);
- return priv->info;
-}
-
-static int soc_camera_platform_init(struct soc_camera_device *icd)
-{
- struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
-
- if (p->power)
- p->power(1);
-
- return 0;
-}
-
-static int soc_camera_platform_release(struct soc_camera_device *icd)
-{
- struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
-
- if (p->power)
- p->power(0);
-
- return 0;
+ struct v4l2_subdev *subdev = platform_get_drvdata(pdev);
+ return container_of(subdev, struct soc_camera_platform_priv, subdev);
}
-static int soc_camera_platform_start_capture(struct soc_camera_device *icd)
+static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd)
{
- struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
- return p->set_capture(p, 1);
+ struct platform_device *pdev =
+ to_platform_device(to_soc_camera_control(icd));
+ return pdev->dev.platform_data;
}
-static int soc_camera_platform_stop_capture(struct soc_camera_device *icd)
+static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
- return p->set_capture(p, 0);
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
+ return p->set_capture(p, enable);
}
static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd,
@@ -75,26 +53,14 @@ static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd,
static unsigned long
soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
{
- struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+ struct soc_camera_platform_info *p = get_info(icd);
return p->bus_param;
}
-static int soc_camera_platform_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
-{
- return 0;
-}
-
-static int soc_camera_platform_set_fmt(struct soc_camera_device *icd,
+static int soc_camera_platform_try_fmt(struct v4l2_subdev *sd,
struct v4l2_format *f)
{
- return 0;
-}
-
-static int soc_camera_platform_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
-{
- struct soc_camera_platform_info *p = soc_camera_platform_get_info(icd);
+ struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd);
struct v4l2_pix_format *pix = &f->fmt.pix;
pix->width = p->format.width;
@@ -102,82 +68,100 @@ static int soc_camera_platform_try_fmt(struct soc_camera_device *icd,
return 0;
}
-static int soc_camera_platform_video_probe(struct soc_camera_device *icd)
+static void soc_camera_platform_video_probe(struct soc_camera_device *icd,
+ struct platform_device *pdev)
{
- struct soc_camera_platform_priv *priv;
- priv = container_of(icd, struct soc_camera_platform_priv, icd);
+ struct soc_camera_platform_priv *priv = get_priv(pdev);
+ struct soc_camera_platform_info *p = pdev->dev.platform_data;
- priv->format.name = priv->info->format_name;
- priv->format.depth = priv->info->format_depth;
- priv->format.fourcc = priv->info->format.pixelformat;
- priv->format.colorspace = priv->info->format.colorspace;
+ priv->format.name = p->format_name;
+ priv->format.depth = p->format_depth;
+ priv->format.fourcc = p->format.pixelformat;
+ priv->format.colorspace = p->format.colorspace;
icd->formats = &priv->format;
icd->num_formats = 1;
-
- return soc_camera_video_start(icd);
}
-static void soc_camera_platform_video_remove(struct soc_camera_device *icd)
-{
- soc_camera_video_stop(icd);
-}
+static struct v4l2_subdev_core_ops platform_subdev_core_ops;
+
+static struct v4l2_subdev_video_ops platform_subdev_video_ops = {
+ .s_stream = soc_camera_platform_s_stream,
+ .try_fmt = soc_camera_platform_try_fmt,
+};
+
+static struct v4l2_subdev_ops platform_subdev_ops = {
+ .core = &platform_subdev_core_ops,
+ .video = &platform_subdev_video_ops,
+};
static struct soc_camera_ops soc_camera_platform_ops = {
- .owner = THIS_MODULE,
- .probe = soc_camera_platform_video_probe,
- .remove = soc_camera_platform_video_remove,
- .init = soc_camera_platform_init,
- .release = soc_camera_platform_release,
- .start_capture = soc_camera_platform_start_capture,
- .stop_capture = soc_camera_platform_stop_capture,
- .set_crop = soc_camera_platform_set_crop,
- .set_fmt = soc_camera_platform_set_fmt,
- .try_fmt = soc_camera_platform_try_fmt,
.set_bus_param = soc_camera_platform_set_bus_param,
.query_bus_param = soc_camera_platform_query_bus_param,
};
static int soc_camera_platform_probe(struct platform_device *pdev)
{
+ struct soc_camera_host *ici;
struct soc_camera_platform_priv *priv;
- struct soc_camera_platform_info *p;
+ struct soc_camera_platform_info *p = pdev->dev.platform_data;
struct soc_camera_device *icd;
int ret;
- p = pdev->dev.platform_data;
if (!p)
return -EINVAL;
+ if (!p->dev) {
+ dev_err(&pdev->dev,
+ "Platform has not set soc_camera_device pointer!\n");
+ return -EINVAL;
+ }
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- priv->info = p;
- platform_set_drvdata(pdev, priv);
+ icd = to_soc_camera_dev(p->dev);
+
+ /* soc-camera convention: control's drvdata points to the subdev */
+ platform_set_drvdata(pdev, &priv->subdev);
+ /* Set the control device reference */
+ dev_set_drvdata(&icd->dev, &pdev->dev);
+
+ icd->y_skip_top = 0;
+ icd->ops = &soc_camera_platform_ops;
+
+ ici = to_soc_camera_host(icd->dev.parent);
- icd = &priv->icd;
- icd->ops = &soc_camera_platform_ops;
- icd->control = &pdev->dev;
- icd->width_min = 0;
- icd->width_max = priv->info->format.width;
- icd->height_min = 0;
- icd->height_max = priv->info->format.height;
- icd->y_skip_top = 0;
- icd->iface = priv->info->iface;
+ soc_camera_platform_video_probe(icd, pdev);
- ret = soc_camera_device_register(icd);
+ v4l2_subdev_init(&priv->subdev, &platform_subdev_ops);
+ v4l2_set_subdevdata(&priv->subdev, p);
+ priv->subdev.grp_id = (__u32)icd;
+ strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE);
+
+ ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev);
if (ret)
- kfree(priv);
+ goto evdrs;
+
+ return ret;
+evdrs:
+ icd->ops = NULL;
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
return ret;
}
static int soc_camera_platform_remove(struct platform_device *pdev)
{
- struct soc_camera_platform_priv *priv = platform_get_drvdata(pdev);
+ struct soc_camera_platform_priv *priv = get_priv(pdev);
+ struct soc_camera_platform_info *p = pdev->dev.platform_data;
+ struct soc_camera_device *icd = to_soc_camera_dev(p->dev);
- soc_camera_device_unregister(&priv->icd);
+ v4l2_device_unregister_subdev(&priv->subdev);
+ icd->ops = NULL;
+ platform_set_drvdata(pdev, NULL);
kfree(priv);
return 0;
}
@@ -185,6 +169,7 @@ static int soc_camera_platform_remove(struct platform_device *pdev)
static struct platform_driver soc_camera_platform_driver = {
.driver = {
.name = "soc_camera_platform",
+ .owner = THIS_MODULE,
},
.probe = soc_camera_platform_probe,
.remove = soc_camera_platform_remove,
@@ -206,3 +191,4 @@ module_exit(soc_camera_platform_module_exit);
MODULE_DESCRIPTION("SoC Camera Platform driver");
MODULE_AUTHOR("Magnus Damm");
MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:soc_camera_platform");
diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c
index 849867ab2..3034a31a5 100644
--- a/linux/drivers/media/video/stk-webcam.c
+++ b/linux/drivers/media/video/stk-webcam.c
@@ -1051,8 +1051,8 @@ static int stk_setup_format(struct stk_camera *dev)
depth = 1;
else
depth = 2;
- while (stk_sizes[i].m != dev->vsettings.mode
- && i < ARRAY_SIZE(stk_sizes))
+ while (i < ARRAY_SIZE(stk_sizes) &&
+ stk_sizes[i].m != dev->vsettings.mode)
i++;
if (i == ARRAY_SIZE(stk_sizes)) {
STK_ERROR("Something is broken in %s\n", __func__);
@@ -1401,7 +1401,6 @@ static int stk_camera_probe(struct usb_interface *interface,
}
stk_create_sysfs_files(&dev->vdev);
- usb_autopm_enable(dev->interface);
return 0;
diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c
index 85b133df4..a4f6c19b7 100644
--- a/linux/drivers/media/video/tuner-core.c
+++ b/linux/drivers/media/video/tuner-core.c
@@ -842,8 +842,8 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
fe_tuner_ops->get_frequency(&t->fe, &abs_freq);
f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
- (abs_freq * 2 + 125/2) / 125 :
- (abs_freq + 62500/2) / 62500;
+ DIV_ROUND_CLOSEST(abs_freq * 2, 125) :
+ DIV_ROUND_CLOSEST(abs_freq, 62500);
return 0;
}
f->frequency = (V4L2_TUNER_RADIO == t->mode) ?
diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c
index 5e3c9f4e2..085b869f5 100644
--- a/linux/drivers/media/video/tveeprom.c
+++ b/linux/drivers/media/video/tveeprom.c
@@ -647,14 +647,14 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
tvee->has_radio = 1;
}
- if (tuner1 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) {
+ if (tuner1 < ARRAY_SIZE(hauppauge_tuner)) {
tvee->tuner_type = hauppauge_tuner[tuner1].id;
t_name1 = hauppauge_tuner[tuner1].name;
} else {
t_name1 = "unknown";
}
- if (tuner2 < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) {
+ if (tuner2 < ARRAY_SIZE(hauppauge_tuner)) {
tvee->tuner2_type = hauppauge_tuner[tuner2].id;
t_name2 = hauppauge_tuner[tuner2].name;
} else {
diff --git a/linux/drivers/media/video/tw9910.c b/linux/drivers/media/video/tw9910.c
index f75787594..db56ae683 100644
--- a/linux/drivers/media/video/tw9910.c
+++ b/linux/drivers/media/video/tw9910.c
@@ -24,7 +24,7 @@
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <media/v4l2-chip-ident.h>
-#include <media/v4l2-common.h>
+#include <media/v4l2-subdev.h>
#include <media/soc_camera.h>
#include <media/tw9910.h>
@@ -223,9 +223,8 @@ struct tw9910_hsync_ctrl {
};
struct tw9910_priv {
+ struct v4l2_subdev subdev;
struct tw9910_video_info *info;
- struct i2c_client *client;
- struct soc_camera_device icd;
const struct tw9910_scale_ctrl *scale;
};
@@ -356,6 +355,12 @@ static const struct tw9910_hsync_ctrl tw9910_hsync_ctrl = {
/*
* general function
*/
+static struct tw9910_priv *to_tw9910(const struct i2c_client *client)
+{
+ return container_of(i2c_get_clientdata(client), struct tw9910_priv,
+ subdev);
+}
+
static int tw9910_set_scale(struct i2c_client *client,
const struct tw9910_scale_ctrl *scale)
{
@@ -509,44 +514,20 @@ tw9910_select_norm(struct soc_camera_device *icd, u32 width, u32 height)
/*
* soc_camera_ops function
*/
-static int tw9910_init(struct soc_camera_device *icd)
-{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
- int ret = 0;
-
- if (priv->info->link.power) {
- ret = priv->info->link.power(&priv->client->dev, 1);
- if (ret < 0)
- return ret;
- }
-
- if (priv->info->link.reset)
- ret = priv->info->link.reset(&priv->client->dev);
-
- return ret;
-}
-
-static int tw9910_release(struct soc_camera_device *icd)
+static int tw9910_s_stream(struct v4l2_subdev *sd, int enable)
{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
- int ret = 0;
-
- if (priv->info->link.power)
- ret = priv->info->link.power(&priv->client->dev, 0);
-
- return ret;
-}
+ struct i2c_client *client = sd->priv;
+ struct tw9910_priv *priv = to_tw9910(client);
-static int tw9910_start_capture(struct soc_camera_device *icd)
-{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ if (!enable)
+ return 0;
if (!priv->scale) {
- dev_err(&icd->dev, "norm select error\n");
+ dev_err(&client->dev, "norm select error\n");
return -EPERM;
}
- dev_dbg(&icd->dev, "%s %dx%d\n",
+ dev_dbg(&client->dev, "%s %dx%d\n",
priv->scale->name,
priv->scale->width,
priv->scale->height);
@@ -554,11 +535,6 @@ static int tw9910_start_capture(struct soc_camera_device *icd)
return 0;
}
-static int tw9910_stop_capture(struct soc_camera_device *icd)
-{
- return 0;
-}
-
static int tw9910_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
@@ -567,8 +543,9 @@ static int tw9910_set_bus_param(struct soc_camera_device *icd,
static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
- struct soc_camera_link *icl = priv->client->dev.platform_data;
+ struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
+ struct tw9910_priv *priv = to_tw9910(client);
+ struct soc_camera_link *icl = to_soc_camera_link(icd);
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
@@ -576,21 +553,11 @@ static unsigned long tw9910_query_bus_param(struct soc_camera_device *icd)
return soc_camera_apply_sensor_flags(icl, flags);
}
-static int tw9910_get_chip_id(struct soc_camera_device *icd,
- struct v4l2_dbg_chip_ident *id)
-{
- id->ident = V4L2_IDENT_TW9910;
- id->revision = 0;
-
- return 0;
-}
-
-static int tw9910_set_std(struct soc_camera_device *icd,
- v4l2_std_id *a)
+static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
{
int ret = -EINVAL;
- if (*a & (V4L2_STD_NTSC | V4L2_STD_PAL))
+ if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL))
ret = 0;
return ret;
@@ -606,17 +573,26 @@ static int tw9910_enum_input(struct soc_camera_device *icd,
return 0;
}
+static int tw9910_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *id)
+{
+ id->ident = V4L2_IDENT_TW9910;
+ id->revision = 0;
+
+ return 0;
+}
+
#ifdef CONFIG_VIDEO_ADV_DEBUG
-static int tw9910_get_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int tw9910_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ struct i2c_client *client = sd->priv;
int ret;
if (reg->reg > 0xff)
return -EINVAL;
- ret = i2c_smbus_read_byte_data(priv->client, reg->reg);
+ ret = i2c_smbus_read_byte_data(client, reg->reg);
if (ret < 0)
return ret;
@@ -628,23 +604,25 @@ static int tw9910_get_register(struct soc_camera_device *icd,
return 0;
}
-static int tw9910_set_register(struct soc_camera_device *icd,
- struct v4l2_dbg_register *reg)
+static int tw9910_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ struct i2c_client *client = sd->priv;
if (reg->reg > 0xff ||
reg->val > 0xff)
return -EINVAL;
- return i2c_smbus_write_byte_data(priv->client, reg->reg, reg->val);
+ return i2c_smbus_write_byte_data(client, reg->reg, reg->val);
}
#endif
-static int tw9910_set_crop(struct soc_camera_device *icd,
- struct v4l2_rect *rect)
+static int tw9910_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ struct v4l2_rect *rect = &a->c;
+ struct i2c_client *client = sd->priv;
+ struct tw9910_priv *priv = to_tw9910(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
int ret = -EINVAL;
u8 val;
@@ -658,8 +636,8 @@ static int tw9910_set_crop(struct soc_camera_device *icd,
/*
* reset hardware
*/
- tw9910_reset(priv->client);
- ret = tw9910_write_array(priv->client, tw9910_default_regs);
+ tw9910_reset(client);
+ ret = tw9910_write_array(client, tw9910_default_regs);
if (ret < 0)
goto tw9910_set_fmt_error;
@@ -670,7 +648,7 @@ static int tw9910_set_crop(struct soc_camera_device *icd,
if (SOCAM_DATAWIDTH_16 == priv->info->buswidth)
val = LEN;
- ret = tw9910_mask_set(priv->client, OPFORM, LEN, val);
+ ret = tw9910_mask_set(client, OPFORM, LEN, val);
if (ret < 0)
goto tw9910_set_fmt_error;
@@ -698,52 +676,139 @@ static int tw9910_set_crop(struct soc_camera_device *icd,
val = 0;
}
- ret = tw9910_mask_set(priv->client, VBICNTL, RTSEL_MASK, val);
+ ret = tw9910_mask_set(client, VBICNTL, RTSEL_MASK, val);
if (ret < 0)
goto tw9910_set_fmt_error;
/*
* set scale
*/
- ret = tw9910_set_scale(priv->client, priv->scale);
+ ret = tw9910_set_scale(client, priv->scale);
if (ret < 0)
goto tw9910_set_fmt_error;
/*
* set cropping
*/
- ret = tw9910_set_cropping(priv->client, &tw9910_cropping_ctrl);
+ ret = tw9910_set_cropping(client, &tw9910_cropping_ctrl);
if (ret < 0)
goto tw9910_set_fmt_error;
/*
* set hsync
*/
- ret = tw9910_set_hsync(priv->client, &tw9910_hsync_ctrl);
+ ret = tw9910_set_hsync(client, &tw9910_hsync_ctrl);
if (ret < 0)
goto tw9910_set_fmt_error;
+ rect->width = priv->scale->width;
+ rect->height = priv->scale->height;
+ rect->left = 0;
+ rect->top = 0;
+
return ret;
tw9910_set_fmt_error:
- tw9910_reset(priv->client);
+ tw9910_reset(client);
priv->scale = NULL;
return ret;
}
-static int tw9910_set_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct i2c_client *client = sd->priv;
+ struct tw9910_priv *priv = to_tw9910(client);
+
+ if (!priv->scale) {
+ int ret;
+ struct v4l2_crop crop = {
+ .c = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ };
+ ret = tw9910_s_crop(sd, &crop);
+ if (ret < 0)
+ return ret;
+ }
+
+ a->c.left = 0;
+ a->c.top = 0;
+ a->c.width = priv->scale->width;
+ a->c.height = priv->scale->height;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
{
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = 768;
+ a->bounds.height = 576;
+ a->defrect.left = 0;
+ a->defrect.top = 0;
+ a->defrect.width = 640;
+ a->defrect.height = 480;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
+static int tw9910_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct tw9910_priv *priv = to_tw9910(client);
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+ if (!priv->scale) {
+ int ret;
+ struct v4l2_crop crop = {
+ .c = {
+ .left = 0,
+ .top = 0,
+ .width = 640,
+ .height = 480,
+ },
+ };
+ ret = tw9910_s_crop(sd, &crop);
+ if (ret < 0)
+ return ret;
+ }
+
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ pix->width = priv->scale->width;
+ pix->height = priv->scale->height;
+ pix->pixelformat = V4L2_PIX_FMT_VYUY;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->field = V4L2_FIELD_INTERLACED;
+
+ return 0;
+}
+
+static int tw9910_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
+{
+ struct i2c_client *client = sd->priv;
+ struct tw9910_priv *priv = to_tw9910(client);
struct v4l2_pix_format *pix = &f->fmt.pix;
- struct v4l2_rect rect = {
- .left = icd->x_current,
- .top = icd->y_current,
- .width = pix->width,
- .height = pix->height,
+ /* See tw9910_s_crop() - no proper cropping support */
+ struct v4l2_crop a = {
+ .c = {
+ .left = 0,
+ .top = 0,
+ .width = pix->width,
+ .height = pix->height,
+ },
};
- int i;
+ int i, ret;
/*
* check color format
@@ -755,19 +820,25 @@ static int tw9910_set_fmt(struct soc_camera_device *icd,
if (i == ARRAY_SIZE(tw9910_color_fmt))
return -EINVAL;
- return tw9910_set_crop(icd, &rect);
+ ret = tw9910_s_crop(sd, &a);
+ if (!ret) {
+ pix->width = priv->scale->width;
+ pix->height = priv->scale->height;
+ }
+ return ret;
}
-static int tw9910_try_fmt(struct soc_camera_device *icd,
- struct v4l2_format *f)
+static int tw9910_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
{
+ struct i2c_client *client = sd->priv;
+ struct soc_camera_device *icd = client->dev.platform_data;
struct v4l2_pix_format *pix = &f->fmt.pix;
const struct tw9910_scale_ctrl *scale;
if (V4L2_FIELD_ANY == pix->field) {
pix->field = V4L2_FIELD_INTERLACED;
} else if (V4L2_FIELD_INTERLACED != pix->field) {
- dev_err(&icd->dev, "Field type invalid.\n");
+ dev_err(&client->dev, "Field type invalid.\n");
return -EINVAL;
}
@@ -784,11 +855,11 @@ static int tw9910_try_fmt(struct soc_camera_device *icd,
return 0;
}
-static int tw9910_video_probe(struct soc_camera_device *icd)
+static int tw9910_video_probe(struct soc_camera_device *icd,
+ struct i2c_client *client)
{
- struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ struct tw9910_priv *priv = to_tw9910(client);
s32 val;
- int ret;
/*
* We must have a parent by now. And it cannot be a wrong one.
@@ -803,7 +874,7 @@ static int tw9910_video_probe(struct soc_camera_device *icd)
*/
if (SOCAM_DATAWIDTH_16 != priv->info->buswidth &&
SOCAM_DATAWIDTH_8 != priv->info->buswidth) {
- dev_err(&icd->dev, "bus width error\n");
+ dev_err(&client->dev, "bus width error\n");
return -ENODEV;
}
@@ -813,54 +884,54 @@ static int tw9910_video_probe(struct soc_camera_device *icd)
/*
* check and show Product ID
*/
- val = i2c_smbus_read_byte_data(priv->client, ID);
+ val = i2c_smbus_read_byte_data(client, ID);
+
if (0x0B != GET_ID(val) ||
0x00 != GET_ReV(val)) {
- dev_err(&icd->dev,
+ dev_err(&client->dev,
"Product ID error %x:%x\n", GET_ID(val), GET_ReV(val));
return -ENODEV;
}
- dev_info(&icd->dev,
+ dev_info(&client->dev,
"tw9910 Product ID %0x:%0x\n", GET_ID(val), GET_ReV(val));
- ret = soc_camera_video_start(icd);
- if (ret < 0)
- return ret;
-
icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL;
icd->vdev->current_norm = V4L2_STD_NTSC;
- return ret;
-}
-
-static void tw9910_video_remove(struct soc_camera_device *icd)
-{
- soc_camera_video_stop(icd);
+ return 0;
}
static struct soc_camera_ops tw9910_ops = {
- .owner = THIS_MODULE,
- .probe = tw9910_video_probe,
- .remove = tw9910_video_remove,
- .init = tw9910_init,
- .release = tw9910_release,
- .start_capture = tw9910_start_capture,
- .stop_capture = tw9910_stop_capture,
- .set_crop = tw9910_set_crop,
- .set_fmt = tw9910_set_fmt,
- .try_fmt = tw9910_try_fmt,
.set_bus_param = tw9910_set_bus_param,
.query_bus_param = tw9910_query_bus_param,
- .get_chip_id = tw9910_get_chip_id,
- .set_std = tw9910_set_std,
.enum_input = tw9910_enum_input,
+};
+
+static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = {
+ .g_chip_ident = tw9910_g_chip_ident,
+ .s_std = tw9910_s_std,
#ifdef CONFIG_VIDEO_ADV_DEBUG
- .get_register = tw9910_get_register,
- .set_register = tw9910_set_register,
+ .g_register = tw9910_g_register,
+ .s_register = tw9910_s_register,
#endif
};
+static struct v4l2_subdev_video_ops tw9910_subdev_video_ops = {
+ .s_stream = tw9910_s_stream,
+ .g_fmt = tw9910_g_fmt,
+ .s_fmt = tw9910_s_fmt,
+ .try_fmt = tw9910_try_fmt,
+ .cropcap = tw9910_cropcap,
+ .g_crop = tw9910_g_crop,
+ .s_crop = tw9910_s_crop,
+};
+
+static struct v4l2_subdev_ops tw9910_subdev_ops = {
+ .core = &tw9910_subdev_core_ops,
+ .video = &tw9910_subdev_video_ops,
+};
+
/*
* i2c_driver function
*/
@@ -875,18 +946,24 @@ static int tw9910_probe(struct i2c_client *client,
{
struct tw9910_priv *priv;
struct tw9910_video_info *info;
- struct soc_camera_device *icd;
- const struct tw9910_scale_ctrl *scale;
- int i, ret;
+ struct soc_camera_device *icd = client->dev.platform_data;
+ struct i2c_adapter *adapter =
+ to_i2c_adapter(client->dev.parent);
+ struct soc_camera_link *icl;
+ int ret;
+
+ if (!icd) {
+ dev_err(&client->dev, "TW9910: missing soc-camera data!\n");
+ return -EINVAL;
+ }
- if (!client->dev.platform_data)
+ icl = to_soc_camera_link(icd);
+ if (!icl)
return -EINVAL;
- info = container_of(client->dev.platform_data,
- struct tw9910_video_info, link);
+ info = container_of(icl, struct tw9910_video_info, link);
- if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent),
- I2C_FUNC_SMBUS_BYTE_DATA)) {
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_err(&client->dev,
"I2C-Adapter doesn't support "
"I2C_FUNC_SMBUS_BYTE_DATA\n");
@@ -898,40 +975,15 @@ static int tw9910_probe(struct i2c_client *client,
return -ENOMEM;
priv->info = info;
- priv->client = client;
- i2c_set_clientdata(client, priv);
- icd = &priv->icd;
+ v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops);
+
icd->ops = &tw9910_ops;
- icd->control = &client->dev;
icd->iface = info->link.bus_id;
- /*
- * set width and height
- */
- icd->width_max = tw9910_ntsc_scales[0].width; /* set default */
- icd->width_min = tw9910_ntsc_scales[0].width;
- icd->height_max = tw9910_ntsc_scales[0].height;
- icd->height_min = tw9910_ntsc_scales[0].height;
-
- scale = tw9910_ntsc_scales;
- for (i = 0; i < ARRAY_SIZE(tw9910_ntsc_scales); i++) {
- icd->width_max = max(scale[i].width, icd->width_max);
- icd->width_min = min(scale[i].width, icd->width_min);
- icd->height_max = max(scale[i].height, icd->height_max);
- icd->height_min = min(scale[i].height, icd->height_min);
- }
- scale = tw9910_pal_scales;
- for (i = 0; i < ARRAY_SIZE(tw9910_pal_scales); i++) {
- icd->width_max = max(scale[i].width, icd->width_max);
- icd->width_min = min(scale[i].width, icd->width_min);
- icd->height_max = max(scale[i].height, icd->height_max);
- icd->height_min = min(scale[i].height, icd->height_min);
- }
-
- ret = soc_camera_device_register(icd);
-
+ ret = tw9910_video_probe(icd, client);
if (ret) {
+ icd->ops = NULL;
i2c_set_clientdata(client, NULL);
kfree(priv);
}
@@ -941,9 +993,10 @@ static int tw9910_probe(struct i2c_client *client,
static int tw9910_remove(struct i2c_client *client)
{
- struct tw9910_priv *priv = i2c_get_clientdata(client);
+ struct tw9910_priv *priv = to_tw9910(client);
+ struct soc_camera_device *icd = client->dev.platform_data;
- soc_camera_device_unregister(&priv->icd);
+ icd->ops = NULL;
i2c_set_clientdata(client, NULL);
kfree(priv);
return 0;
diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c
index fa13b6765..09b2e4ad5 100644
--- a/linux/drivers/media/video/uvc/uvc_driver.c
+++ b/linux/drivers/media/video/uvc/uvc_driver.c
@@ -1996,7 +1996,7 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* ViMicro Vega */
- { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0ac8,
.idProduct = 0x332d,
@@ -2005,7 +2005,7 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_FIX_BANDWIDTH },
/* ViMicro - Minoru3D */
- { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0ac8,
.idProduct = 0x3410,
@@ -2014,7 +2014,7 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_FIX_BANDWIDTH },
/* ViMicro Venus - Minoru3D */
- { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0ac8,
.idProduct = 0x3420,
diff --git a/linux/drivers/media/video/v4l1-compat.c b/linux/drivers/media/video/v4l1-compat.c
index ee3991f2f..2225243ad 100644
--- a/linux/drivers/media/video/v4l1-compat.c
+++ b/linux/drivers/media/video/v4l1-compat.c
@@ -77,9 +77,8 @@ get_v4l_control(struct file *file,
dprintk("VIDIOC_G_CTRL: %d\n", err);
return 0;
}
- return ((ctrl2.value - qctrl2.minimum) * 65535
- + (qctrl2.maximum - qctrl2.minimum) / 2)
- / (qctrl2.maximum - qctrl2.minimum);
+ return DIV_ROUND_CLOSEST((ctrl2.value-qctrl2.minimum) * 65535,
+ qctrl2.maximum - qctrl2.minimum);
}
return 0;
}
diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c
index 80c94dc5a..d746d9555 100644
--- a/linux/drivers/media/video/v4l2-ioctl.c
+++ b/linux/drivers/media/video/v4l2-ioctl.c
@@ -1088,8 +1088,10 @@ static long __video_do_ioctl(struct file *file,
/* Calls the specific handler */
if (ops->vidioc_g_std)
ret = ops->vidioc_g_std(file, fh, id);
- else
+ else if (vfd->current_norm)
*id = vfd->current_norm;
+ else
+ ret = -EINVAL;
if (!ret)
dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id);
@@ -1560,12 +1562,19 @@ static long __video_do_ioctl(struct file *file,
break;
ret = ops->vidioc_g_parm(file, fh, p);
} else {
+ v4l2_std_id std = vfd->current_norm;
+
if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- v4l2_video_std_frame_period(vfd->current_norm,
- &p->parm.capture.timeperframe);
ret = 0;
+ if (ops->vidioc_g_std)
+ ret = ops->vidioc_g_std(file, fh, &std);
+ else if (std == 0)
+ ret = -EINVAL;
+ if (ret == 0)
+ v4l2_video_std_frame_period(std,
+ &p->parm.capture.timeperframe);
}
dbgarg(cmd, "type=%d\n", p->type);
diff --git a/linux/drivers/media/video/zoran/zoran_card.c b/linux/drivers/media/video/zoran/zoran_card.c
index 74509d627..ed1b04303 100644
--- a/linux/drivers/media/video/zoran/zoran_card.c
+++ b/linux/drivers/media/video/zoran/zoran_card.c
@@ -1169,7 +1169,7 @@ zoran_setup_videocodec (struct zoran *zr,
m->type = 0;
m->flags = CODEC_FLAG_ENCODER | CODEC_FLAG_DECODER;
- strncpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
+ strlcpy(m->name, ZR_DEVNAME(zr), sizeof(m->name));
m->data = zr;
switch (type)
diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c
index c298ce625..ee5534230 100644
--- a/linux/drivers/media/video/zr364xx.c
+++ b/linux/drivers/media/video/zr364xx.c
@@ -1367,7 +1367,7 @@ static int zr364xx_release(struct file *file)
for (i = 0; i < 2; i++) {
err =
send_control_msg(udev, 1, init[cam->method][i].value,
- 0, init[i][cam->method].bytes,
+ 0, init[cam->method][i].bytes,
init[cam->method][i].size);
if (err < 0) {
dev_err(&udev->dev, "error during release sequence\n");