|
@@ -46,7 +46,6 @@
|
|
|
#include <linux/videodev2.h>
|
|
|
#include <media/v4l2-device.h>
|
|
|
#include <media/v4l2-ctrls.h>
|
|
|
-#include <media/v4l2-chip-ident.h>
|
|
|
#include <media/saa7115.h>
|
|
|
#include <asm/div64.h>
|
|
|
|
|
@@ -63,6 +62,16 @@ module_param(debug, bool, 0644);
|
|
|
MODULE_PARM_DESC(debug, "Debug level (0-1)");
|
|
|
|
|
|
|
|
|
+enum saa711x_model {
|
|
|
+ SAA7111A,
|
|
|
+ SAA7111,
|
|
|
+ SAA7113,
|
|
|
+ GM7113C,
|
|
|
+ SAA7114,
|
|
|
+ SAA7115,
|
|
|
+ SAA7118,
|
|
|
+};
|
|
|
+
|
|
|
struct saa711x_state {
|
|
|
struct v4l2_subdev sd;
|
|
|
struct v4l2_ctrl_handler hdl;
|
|
@@ -80,7 +89,7 @@ struct saa711x_state {
|
|
|
int radio;
|
|
|
int width;
|
|
|
int height;
|
|
|
- u32 ident;
|
|
|
+ enum saa711x_model ident;
|
|
|
u32 audclk_freq;
|
|
|
u32 crystal_freq;
|
|
|
bool ucgc;
|
|
@@ -111,10 +120,10 @@ static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value)
|
|
|
/* Sanity routine to check if a register is present */
|
|
|
static int saa711x_has_reg(const int id, const u8 reg)
|
|
|
{
|
|
|
- if (id == V4L2_IDENT_SAA7111)
|
|
|
+ if (id == SAA7111)
|
|
|
return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
|
|
|
(reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
|
|
|
- if (id == V4L2_IDENT_SAA7111A)
|
|
|
+ if (id == SAA7111A)
|
|
|
return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
|
|
|
reg != 0x14 && reg != 0x18 && reg != 0x19 &&
|
|
|
reg != 0x1d && reg != 0x1e;
|
|
@@ -127,16 +136,18 @@ static int saa711x_has_reg(const int id, const u8 reg)
|
|
|
return 0;
|
|
|
|
|
|
switch (id) {
|
|
|
- case V4L2_IDENT_SAA7113:
|
|
|
+ case GM7113C:
|
|
|
+ return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && reg < 0x20;
|
|
|
+ case SAA7113:
|
|
|
return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
|
|
|
reg != 0x5d && reg < 0x63;
|
|
|
- case V4L2_IDENT_SAA7114:
|
|
|
+ case SAA7114:
|
|
|
return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) &&
|
|
|
(reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 &&
|
|
|
reg != 0x81 && reg < 0xf0;
|
|
|
- case V4L2_IDENT_SAA7115:
|
|
|
+ case SAA7115:
|
|
|
return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
|
|
|
- case V4L2_IDENT_SAA7118:
|
|
|
+ case SAA7118:
|
|
|
return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) &&
|
|
|
(reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 &&
|
|
|
(reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0;
|
|
@@ -214,7 +225,10 @@ static const unsigned char saa7111_init[] = {
|
|
|
0x00, 0x00
|
|
|
};
|
|
|
|
|
|
-/* SAA7113 init codes */
|
|
|
+/* SAA7113/GM7113C init codes
|
|
|
+ * It's important that R_14... R_17 == 0x00
|
|
|
+ * for the gm7113c chip to deliver stable video
|
|
|
+ */
|
|
|
static const unsigned char saa7113_init[] = {
|
|
|
R_01_INC_DELAY, 0x08,
|
|
|
R_02_INPUT_CNTL_1, 0xc2,
|
|
@@ -448,6 +462,24 @@ static const unsigned char saa7115_cfg_50hz_video[] = {
|
|
|
|
|
|
/* ============== SAA7715 VIDEO templates (end) ======= */
|
|
|
|
|
|
+/* ============== GM7113C VIDEO templates ============= */
|
|
|
+static const unsigned char gm7113c_cfg_60hz_video[] = {
|
|
|
+ R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
|
|
|
+ R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */
|
|
|
+
|
|
|
+ 0x00, 0x00
|
|
|
+};
|
|
|
+
|
|
|
+static const unsigned char gm7113c_cfg_50hz_video[] = {
|
|
|
+ R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */
|
|
|
+ R_0E_CHROMA_CNTL_1, 0x07,
|
|
|
+
|
|
|
+ 0x00, 0x00
|
|
|
+};
|
|
|
+
|
|
|
+/* ============== GM7113C VIDEO templates (end) ======= */
|
|
|
+
|
|
|
+
|
|
|
static const unsigned char saa7115_cfg_vbi_on[] = {
|
|
|
R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
|
|
|
R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
|
|
@@ -932,11 +964,17 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
|
|
|
// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
|
|
|
if (std & V4L2_STD_525_60) {
|
|
|
v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n");
|
|
|
- saa711x_writeregs(sd, saa7115_cfg_60hz_video);
|
|
|
+ if (state->ident == GM7113C)
|
|
|
+ saa711x_writeregs(sd, gm7113c_cfg_60hz_video);
|
|
|
+ else
|
|
|
+ saa711x_writeregs(sd, saa7115_cfg_60hz_video);
|
|
|
saa711x_set_size(sd, 720, 480);
|
|
|
} else {
|
|
|
v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n");
|
|
|
- saa711x_writeregs(sd, saa7115_cfg_50hz_video);
|
|
|
+ if (state->ident == GM7113C)
|
|
|
+ saa711x_writeregs(sd, gm7113c_cfg_50hz_video);
|
|
|
+ else
|
|
|
+ saa711x_writeregs(sd, saa7115_cfg_50hz_video);
|
|
|
saa711x_set_size(sd, 720, 576);
|
|
|
}
|
|
|
|
|
@@ -949,7 +987,8 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
|
|
|
011 NTSC N (3.58MHz) PAL M (3.58MHz)
|
|
|
100 reserved NTSC-Japan (3.58MHz)
|
|
|
*/
|
|
|
- if (state->ident <= V4L2_IDENT_SAA7113) {
|
|
|
+ if (state->ident <= SAA7113 ||
|
|
|
+ state->ident == GM7113C) {
|
|
|
u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f;
|
|
|
|
|
|
if (std == V4L2_STD_PAL_M) {
|
|
@@ -968,9 +1007,8 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
|
|
|
/* restart task B if needed */
|
|
|
int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10;
|
|
|
|
|
|
- if (taskb && state->ident == V4L2_IDENT_SAA7114) {
|
|
|
+ if (taskb && state->ident == SAA7114)
|
|
|
saa711x_writeregs(sd, saa7115_cfg_vbi_on);
|
|
|
- }
|
|
|
|
|
|
/* switch audio mode too! */
|
|
|
saa711x_s_clock_freq(sd, state->audclk_freq);
|
|
@@ -992,7 +1030,7 @@ static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_forma
|
|
|
|
|
|
#else
|
|
|
/* SAA7113 and SAA7118 also should support VBI - Need testing */
|
|
|
- if (state->ident != V4L2_IDENT_SAA7115)
|
|
|
+ if (state->ident != SAA7115)
|
|
|
return;
|
|
|
#endif
|
|
|
|
|
@@ -1214,13 +1252,14 @@ static int saa711x_s_routing(struct v4l2_subdev *sd,
|
|
|
u32 input, u32 output, u32 config)
|
|
|
{
|
|
|
struct saa711x_state *state = to_state(sd);
|
|
|
- u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0;
|
|
|
+ u8 mask = (state->ident <= SAA7111A) ? 0xf8 : 0xf0;
|
|
|
|
|
|
v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n",
|
|
|
input, output);
|
|
|
|
|
|
/* saa7111/3 does not have these inputs */
|
|
|
- if (state->ident <= V4L2_IDENT_SAA7113 &&
|
|
|
+ if ((state->ident <= SAA7113 ||
|
|
|
+ state->ident == GM7113C) &&
|
|
|
(input == SAA7115_COMPOSITE4 ||
|
|
|
input == SAA7115_COMPOSITE5)) {
|
|
|
return -EINVAL;
|
|
@@ -1235,7 +1274,7 @@ static int saa711x_s_routing(struct v4l2_subdev *sd,
|
|
|
state->input = input;
|
|
|
|
|
|
/* saa7111 has slightly different input numbering */
|
|
|
- if (state->ident <= V4L2_IDENT_SAA7111A) {
|
|
|
+ if (state->ident <= SAA7111A) {
|
|
|
if (input >= SAA7115_COMPOSITE4)
|
|
|
input -= 2;
|
|
|
/* saa7111 specific */
|
|
@@ -1258,13 +1297,13 @@ static int saa711x_s_routing(struct v4l2_subdev *sd,
|
|
|
(state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
|
|
|
|
|
|
state->output = output;
|
|
|
- if (state->ident == V4L2_IDENT_SAA7114 ||
|
|
|
- state->ident == V4L2_IDENT_SAA7115) {
|
|
|
+ if (state->ident == SAA7114 ||
|
|
|
+ state->ident == SAA7115) {
|
|
|
saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK,
|
|
|
(saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) |
|
|
|
(state->output & 0x01));
|
|
|
}
|
|
|
- if (state->ident > V4L2_IDENT_SAA7111A) {
|
|
|
+ if (state->ident > SAA7111A) {
|
|
|
if (config & SAA7115_IDQ_IS_DEFAULT)
|
|
|
saa711x_write(sd, R_85_I_PORT_SIGNAL_POLAR, 0x20);
|
|
|
else
|
|
@@ -1277,7 +1316,7 @@ static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val)
|
|
|
{
|
|
|
struct saa711x_state *state = to_state(sd);
|
|
|
|
|
|
- if (state->ident > V4L2_IDENT_SAA7111A)
|
|
|
+ if (state->ident > SAA7111A)
|
|
|
return -EINVAL;
|
|
|
saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) |
|
|
|
(val ? 0x80 : 0));
|
|
@@ -1367,7 +1406,7 @@ static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
|
|
|
|
|
|
reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
|
|
|
|
|
|
- if (state->ident == V4L2_IDENT_SAA7115) {
|
|
|
+ if (state->ident == SAA7115) {
|
|
|
reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
|
|
|
|
|
|
v4l2_dbg(1, debug, sd, "Status byte 1 (0x1e)=0x%02x\n", reg1e);
|
|
@@ -1389,6 +1428,7 @@ static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
|
|
|
*std &= V4L2_STD_SECAM;
|
|
|
break;
|
|
|
default:
|
|
|
+ *std = V4L2_STD_UNKNOWN;
|
|
|
/* Can't detect anything */
|
|
|
break;
|
|
|
}
|
|
@@ -1397,8 +1437,10 @@ static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
|
|
|
v4l2_dbg(1, debug, sd, "Status byte 2 (0x1f)=0x%02x\n", reg1f);
|
|
|
|
|
|
/* horizontal/vertical not locked */
|
|
|
- if (reg1f & 0x40)
|
|
|
+ if (reg1f & 0x40) {
|
|
|
+ *std = V4L2_STD_UNKNOWN;
|
|
|
goto ret;
|
|
|
+ }
|
|
|
|
|
|
if (reg1f & 0x20)
|
|
|
*std &= V4L2_STD_525_60;
|
|
@@ -1418,7 +1460,7 @@ static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
|
|
|
int reg1f;
|
|
|
|
|
|
*status = V4L2_IN_ST_NO_SIGNAL;
|
|
|
- if (state->ident == V4L2_IDENT_SAA7115)
|
|
|
+ if (state->ident == SAA7115)
|
|
|
reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
|
|
|
reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
|
|
|
if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
|
|
@@ -1429,12 +1471,6 @@ static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
|
static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
|
|
|
{
|
|
|
- struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
-
|
|
|
- if (!v4l2_chip_match_i2c_client(client, ®->match))
|
|
|
- return -EINVAL;
|
|
|
- if (!capable(CAP_SYS_ADMIN))
|
|
|
- return -EPERM;
|
|
|
reg->val = saa711x_read(sd, reg->reg & 0xff);
|
|
|
reg->size = 1;
|
|
|
return 0;
|
|
@@ -1442,25 +1478,11 @@ static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
|
|
|
|
|
|
static int saa711x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg)
|
|
|
{
|
|
|
- struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
-
|
|
|
- if (!v4l2_chip_match_i2c_client(client, ®->match))
|
|
|
- return -EINVAL;
|
|
|
- if (!capable(CAP_SYS_ADMIN))
|
|
|
- return -EPERM;
|
|
|
saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff);
|
|
|
return 0;
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
|
|
|
-{
|
|
|
- struct saa711x_state *state = to_state(sd);
|
|
|
- struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
-
|
|
|
- return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0);
|
|
|
-}
|
|
|
-
|
|
|
static int saa711x_log_status(struct v4l2_subdev *sd)
|
|
|
{
|
|
|
struct saa711x_state *state = to_state(sd);
|
|
@@ -1469,7 +1491,7 @@ static int saa711x_log_status(struct v4l2_subdev *sd)
|
|
|
int vcr;
|
|
|
|
|
|
v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq);
|
|
|
- if (state->ident != V4L2_IDENT_SAA7115) {
|
|
|
+ if (state->ident != SAA7115) {
|
|
|
/* status for the saa7114 */
|
|
|
reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
|
|
|
signalOk = (reg1f & 0xc1) == 0x81;
|
|
@@ -1520,7 +1542,6 @@ static const struct v4l2_ctrl_ops saa711x_ctrl_ops = {
|
|
|
|
|
|
static const struct v4l2_subdev_core_ops saa711x_core_ops = {
|
|
|
.log_status = saa711x_log_status,
|
|
|
- .g_chip_ident = saa711x_g_chip_ident,
|
|
|
.g_ext_ctrls = v4l2_subdev_g_ext_ctrls,
|
|
|
.try_ext_ctrls = v4l2_subdev_try_ext_ctrls,
|
|
|
.s_ext_ctrls = v4l2_subdev_s_ext_ctrls,
|
|
@@ -1571,55 +1592,145 @@ static const struct v4l2_subdev_ops saa711x_ops = {
|
|
|
.vbi = &saa711x_vbi_ops,
|
|
|
};
|
|
|
|
|
|
+#define CHIP_VER_SIZE 16
|
|
|
+
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
|
|
-static int saa711x_probe(struct i2c_client *client,
|
|
|
- const struct i2c_device_id *id)
|
|
|
+/**
|
|
|
+ * saa711x_detect_chip - Detects the saa711x (or clone) variant
|
|
|
+ * @client: I2C client structure.
|
|
|
+ * @id: I2C device ID structure.
|
|
|
+ * @name: Name of the device to be filled.
|
|
|
+ *
|
|
|
+ * Detects the Philips/NXP saa711x chip, or some clone of it.
|
|
|
+ * if 'id' is NULL or id->driver_data is equal to 1, it auto-probes
|
|
|
+ * the analog demod.
|
|
|
+ * If the tuner is not found, it returns -ENODEV.
|
|
|
+ * If auto-detection is disabled and the tuner doesn't match what it was
|
|
|
+ * requred, it returns -EINVAL and fills 'name'.
|
|
|
+ * If the chip is found, it returns the chip ID and fills 'name'.
|
|
|
+ */
|
|
|
+static int saa711x_detect_chip(struct i2c_client *client,
|
|
|
+ const struct i2c_device_id *id,
|
|
|
+ char *name)
|
|
|
{
|
|
|
- struct saa711x_state *state;
|
|
|
- struct v4l2_subdev *sd;
|
|
|
- struct v4l2_ctrl_handler *hdl;
|
|
|
- int i;
|
|
|
- char name[17];
|
|
|
+ char chip_ver[CHIP_VER_SIZE];
|
|
|
char chip_id;
|
|
|
- int autodetect = !id || id->driver_data == 1;
|
|
|
+ int i;
|
|
|
+ int autodetect;
|
|
|
|
|
|
- /* Check if the adapter supports the needed features */
|
|
|
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
|
|
- return -EIO;
|
|
|
+ autodetect = !id || id->driver_data == 1;
|
|
|
|
|
|
- for (i = 0; i < 0x0f; i++) {
|
|
|
+ /* Read the chip version register */
|
|
|
+ for (i = 0; i < CHIP_VER_SIZE; i++) {
|
|
|
i2c_smbus_write_byte_data(client, 0, i);
|
|
|
- name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0';
|
|
|
+ chip_ver[i] = i2c_smbus_read_byte_data(client, 0);
|
|
|
+ name[i] = (chip_ver[i] & 0x0f) + '0';
|
|
|
if (name[i] > '9')
|
|
|
name[i] += 'a' - '9' - 1;
|
|
|
}
|
|
|
name[i] = '\0';
|
|
|
|
|
|
- chip_id = name[5];
|
|
|
+ /* Check if it is a Philips/NXP chip */
|
|
|
+ if (!memcmp(name + 1, "f711", 4)) {
|
|
|
+ chip_id = name[5];
|
|
|
+ snprintf(name, CHIP_VER_SIZE, "saa711%c", chip_id);
|
|
|
|
|
|
- /* Check whether this chip is part of the saa711x series */
|
|
|
- if (memcmp(name + 1, "f711", 4)) {
|
|
|
- v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n",
|
|
|
- client->addr << 1, name);
|
|
|
- return -ENODEV;
|
|
|
+ if (!autodetect && strcmp(name, id->name))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ switch (chip_id) {
|
|
|
+ case '1':
|
|
|
+ if (chip_ver[0] & 0xf0) {
|
|
|
+ snprintf(name, CHIP_VER_SIZE, "saa711%ca", chip_id);
|
|
|
+ v4l_info(client, "saa7111a variant found\n");
|
|
|
+ return SAA7111A;
|
|
|
+ }
|
|
|
+ return SAA7111;
|
|
|
+ case '3':
|
|
|
+ return SAA7113;
|
|
|
+ case '4':
|
|
|
+ return SAA7114;
|
|
|
+ case '5':
|
|
|
+ return SAA7115;
|
|
|
+ case '8':
|
|
|
+ return SAA7118;
|
|
|
+ default:
|
|
|
+ v4l2_info(client,
|
|
|
+ "WARNING: Philips/NXP chip unknown - Falling back to saa7111\n");
|
|
|
+ return SAA7111;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- /* Safety check */
|
|
|
- if (!autodetect && id->name[6] != chip_id) {
|
|
|
- v4l_warn(client, "found saa711%c while %s was expected\n",
|
|
|
- chip_id, id->name);
|
|
|
+ /* Check if it is a gm7113c */
|
|
|
+ if (!memcmp(name, "0000", 4)) {
|
|
|
+ chip_id = 0;
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
+ chip_id = chip_id << 1;
|
|
|
+ chip_id |= (chip_ver[i] & 0x80) ? 1 : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Note: From the datasheet, only versions 1 and 2
|
|
|
+ * exists. However, tests on a device labeled as:
|
|
|
+ * "GM7113C 1145" returned "10" on all 16 chip
|
|
|
+ * version (reg 0x00) reads. So, we need to also
|
|
|
+ * accept at least verion 0. For now, let's just
|
|
|
+ * assume that a device that returns "0000" for
|
|
|
+ * the lower nibble is a gm7113c.
|
|
|
+ */
|
|
|
+
|
|
|
+ strlcpy(name, "gm7113c", CHIP_VER_SIZE);
|
|
|
+
|
|
|
+ if (!autodetect && strcmp(name, id->name))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ v4l_dbg(1, debug, client,
|
|
|
+ "It seems to be a %s chip (%*ph) @ 0x%x.\n",
|
|
|
+ name, 16, chip_ver, client->addr << 1);
|
|
|
+
|
|
|
+ return GM7113C;
|
|
|
}
|
|
|
- snprintf(client->name, sizeof(client->name), "saa711%c", chip_id);
|
|
|
- v4l_info(client, "saa711%c found (%s) @ 0x%x (%s)\n", chip_id, name,
|
|
|
- client->addr << 1, client->adapter->name);
|
|
|
|
|
|
- state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
|
|
|
+ /* Chip was not discovered. Return its ID and don't bind */
|
|
|
+ v4l_dbg(1, debug, client, "chip %*ph @ 0x%x is unknown.\n",
|
|
|
+ 16, chip_ver, client->addr << 1);
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+
|
|
|
+static int saa711x_probe(struct i2c_client *client,
|
|
|
+ const struct i2c_device_id *id)
|
|
|
+{
|
|
|
+ struct saa711x_state *state;
|
|
|
+ struct v4l2_subdev *sd;
|
|
|
+ struct v4l2_ctrl_handler *hdl;
|
|
|
+ int ident;
|
|
|
+ char name[CHIP_VER_SIZE + 1];
|
|
|
+
|
|
|
+ /* Check if the adapter supports the needed features */
|
|
|
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ ident = saa711x_detect_chip(client, id, name);
|
|
|
+ if (ident == -EINVAL) {
|
|
|
+ /* Chip exists, but doesn't match */
|
|
|
+ v4l_warn(client, "found %s while %s was expected\n",
|
|
|
+ name, id->name);
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+ if (ident < 0)
|
|
|
+ return ident;
|
|
|
+
|
|
|
+ strlcpy(client->name, name, sizeof(client->name));
|
|
|
+
|
|
|
+ state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
|
|
|
if (state == NULL)
|
|
|
return -ENOMEM;
|
|
|
sd = &state->sd;
|
|
|
v4l2_i2c_subdev_init(sd, client, &saa711x_ops);
|
|
|
|
|
|
+ v4l_info(client, "%s found @ 0x%x (%s)\n", name,
|
|
|
+ client->addr << 1, client->adapter->name);
|
|
|
hdl = &state->hdl;
|
|
|
v4l2_ctrl_handler_init(hdl, 6);
|
|
|
/* add in ascending ID order */
|
|
@@ -1640,7 +1751,6 @@ static int saa711x_probe(struct i2c_client *client,
|
|
|
int err = hdl->error;
|
|
|
|
|
|
v4l2_ctrl_handler_free(hdl);
|
|
|
- kfree(state);
|
|
|
return err;
|
|
|
}
|
|
|
v4l2_ctrl_auto_cluster(2, &state->agc, 0, true);
|
|
@@ -1649,31 +1759,7 @@ static int saa711x_probe(struct i2c_client *client,
|
|
|
state->output = SAA7115_IPORT_ON;
|
|
|
state->enable = 1;
|
|
|
state->radio = 0;
|
|
|
- switch (chip_id) {
|
|
|
- case '1':
|
|
|
- state->ident = V4L2_IDENT_SAA7111;
|
|
|
- if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) {
|
|
|
- v4l_info(client, "saa7111a variant found\n");
|
|
|
- state->ident = V4L2_IDENT_SAA7111A;
|
|
|
- }
|
|
|
- break;
|
|
|
- case '3':
|
|
|
- state->ident = V4L2_IDENT_SAA7113;
|
|
|
- break;
|
|
|
- case '4':
|
|
|
- state->ident = V4L2_IDENT_SAA7114;
|
|
|
- break;
|
|
|
- case '5':
|
|
|
- state->ident = V4L2_IDENT_SAA7115;
|
|
|
- break;
|
|
|
- case '8':
|
|
|
- state->ident = V4L2_IDENT_SAA7118;
|
|
|
- break;
|
|
|
- default:
|
|
|
- state->ident = V4L2_IDENT_SAA7111;
|
|
|
- v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n");
|
|
|
- break;
|
|
|
- }
|
|
|
+ state->ident = ident;
|
|
|
|
|
|
state->audclk_freq = 48000;
|
|
|
|
|
@@ -1682,18 +1768,19 @@ static int saa711x_probe(struct i2c_client *client,
|
|
|
/* init to 60hz/48khz */
|
|
|
state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
|
|
|
switch (state->ident) {
|
|
|
- case V4L2_IDENT_SAA7111:
|
|
|
- case V4L2_IDENT_SAA7111A:
|
|
|
+ case SAA7111:
|
|
|
+ case SAA7111A:
|
|
|
saa711x_writeregs(sd, saa7111_init);
|
|
|
break;
|
|
|
- case V4L2_IDENT_SAA7113:
|
|
|
+ case GM7113C:
|
|
|
+ case SAA7113:
|
|
|
saa711x_writeregs(sd, saa7113_init);
|
|
|
break;
|
|
|
default:
|
|
|
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
|
|
|
saa711x_writeregs(sd, saa7115_init_auto_input);
|
|
|
}
|
|
|
- if (state->ident > V4L2_IDENT_SAA7111A)
|
|
|
+ if (state->ident > SAA7111A)
|
|
|
saa711x_writeregs(sd, saa7115_init_misc);
|
|
|
saa711x_set_v4lstd(sd, V4L2_STD_NTSC);
|
|
|
v4l2_ctrl_handler_setup(hdl);
|
|
@@ -1712,7 +1799,6 @@ static int saa711x_remove(struct i2c_client *client)
|
|
|
|
|
|
v4l2_device_unregister_subdev(sd);
|
|
|
v4l2_ctrl_handler_free(sd->ctrl_handler);
|
|
|
- kfree(to_state(sd));
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1723,6 +1809,7 @@ static const struct i2c_device_id saa711x_id[] = {
|
|
|
{ "saa7114", 0 },
|
|
|
{ "saa7115", 0 },
|
|
|
{ "saa7118", 0 },
|
|
|
+ { "gm7113c", 0 },
|
|
|
{ }
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(i2c, saa711x_id);
|