diff options
Diffstat (limited to 'drivers/media/video/cs53l32a.c')
-rw-r--r-- | drivers/media/video/cs53l32a.c | 188 |
1 files changed, 112 insertions, 76 deletions
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index c4444500b330..cb65d519cf78 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -27,7 +27,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -47,84 +47,104 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ -static int cs53l32a_write(struct i2c_client *client, u8 reg, u8 value) +static int cs53l32a_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } -static int cs53l32a_read(struct i2c_client *client, u8 reg) +static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } -static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) +static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = (cs53l32a_read(client, 0x01) >> 4) & 3; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - /* There are 2 physical inputs, but the second input can be - placed in two modes, the first mode bypasses the PGA (gain), - the second goes through the PGA. Hence there are three - possible inputs to choose from. */ - if (route->input > 2) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - cs53l32a_write(client, 0x01, 0x01 + (route->input << 4)); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - ctrl->value = (cs53l32a_read(client, 0x03) & 0xc0) != 0; - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - ctrl->value = (s8)cs53l32a_read(client, 0x04); - break; - - case VIDIOC_S_CTRL: - if (ctrl->id == V4L2_CID_AUDIO_MUTE) { - cs53l32a_write(client, 0x03, ctrl->value ? 0xf0 : 0x30); - break; - } - if (ctrl->id != V4L2_CID_AUDIO_VOLUME) - return -EINVAL; - if (ctrl->value > 12 || ctrl->value < -96) - return -EINVAL; - cs53l32a_write(client, 0x04, (u8) ctrl->value); - cs53l32a_write(client, 0x05, (u8) ctrl->value); - break; - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_CS53l32A, 0); - - case VIDIOC_LOG_STATUS: - { - u8 v = cs53l32a_read(client, 0x01); - u8 m = cs53l32a_read(client, 0x03); - s8 vol = cs53l32a_read(client, 0x04); - - v4l_info(client, "Input: %d%s\n", (v >> 4) & 3, - (m & 0xC0) ? " (muted)" : ""); - v4l_info(client, "Volume: %d dB\n", vol); - break; - } - - default: + /* There are 2 physical inputs, but the second input can be + placed in two modes, the first mode bypasses the PGA (gain), + the second goes through the PGA. Hence there are three + possible inputs to choose from. */ + if (route->input > 2) { + v4l2_err(sd, "Invalid input %d.\n", route->input); return -EINVAL; } + cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4)); + return 0; +} + +static int cs53l32a_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + ctrl->value = (cs53l32a_read(sd, 0x03) & 0xc0) != 0; + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + ctrl->value = (s8)cs53l32a_read(sd, 0x04); + return 0; +} + +static int cs53l32a_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + if (ctrl->id == V4L2_CID_AUDIO_MUTE) { + cs53l32a_write(sd, 0x03, ctrl->value ? 0xf0 : 0x30); + return 0; + } + if (ctrl->id != V4L2_CID_AUDIO_VOLUME) + return -EINVAL; + if (ctrl->value > 12 || ctrl->value < -96) + return -EINVAL; + cs53l32a_write(sd, 0x04, (u8) ctrl->value); + cs53l32a_write(sd, 0x05, (u8) ctrl->value); return 0; } +static int cs53l32a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, + chip, V4L2_IDENT_CS53l32A, 0); +} + +static int cs53l32a_log_status(struct v4l2_subdev *sd) +{ + u8 v = cs53l32a_read(sd, 0x01); + u8 m = cs53l32a_read(sd, 0x03); + s8 vol = cs53l32a_read(sd, 0x04); + + v4l2_info(sd, "Input: %d%s\n", (v >> 4) & 3, + (m & 0xC0) ? " (muted)" : ""); + v4l2_info(sd, "Volume: %d dB\n", vol); + return 0; +} + +static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { + .log_status = cs53l32a_log_status, + .g_chip_ident = cs53l32a_g_chip_ident, + .g_ctrl = cs53l32a_g_ctrl, + .s_ctrl = cs53l32a_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops cs53l32a_audio_ops = { + .s_routing = cs53l32a_s_routing, +}; + +static const struct v4l2_subdev_ops cs53l32a_ops = { + .core = &cs53l32a_core_ops, + .audio = &cs53l32a_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -137,6 +157,7 @@ static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) static int cs53l32a_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_subdev *sd; int i; /* Check if the adapter supports the needed features */ @@ -149,32 +170,46 @@ static int cs53l32a_probe(struct i2c_client *client, v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); + sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + v4l2_i2c_subdev_init(sd, client, &cs53l32a_ops); + for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(client, i); + u8 v = cs53l32a_read(sd, i); - v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); } /* Set cs53l32a internal register for Adaptec 2010/2410 setup */ - cs53l32a_write(client, 0x01, (u8) 0x21); - cs53l32a_write(client, 0x02, (u8) 0x29); - cs53l32a_write(client, 0x03, (u8) 0x30); - cs53l32a_write(client, 0x04, (u8) 0x00); - cs53l32a_write(client, 0x05, (u8) 0x00); - cs53l32a_write(client, 0x06, (u8) 0x00); - cs53l32a_write(client, 0x07, (u8) 0x00); + cs53l32a_write(sd, 0x01, (u8) 0x21); + cs53l32a_write(sd, 0x02, (u8) 0x29); + cs53l32a_write(sd, 0x03, (u8) 0x30); + cs53l32a_write(sd, 0x04, (u8) 0x00); + cs53l32a_write(sd, 0x05, (u8) 0x00); + cs53l32a_write(sd, 0x06, (u8) 0x00); + cs53l32a_write(sd, 0x07, (u8) 0x00); /* Display results, should be 0x21,0x29,0x30,0x00,0x00,0x00,0x00 */ for (i = 1; i <= 7; i++) { - u8 v = cs53l32a_read(client, i); + u8 v = cs53l32a_read(sd, i); - v4l_dbg(1, debug, client, "Read Reg %d %02x\n", i, v); + v4l2_dbg(1, debug, sd, "Read Reg %d %02x\n", i, v); } return 0; } +static int cs53l32a_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + return 0; +} + static const struct i2c_device_id cs53l32a_id[] = { { "cs53l32a", 0 }, { } @@ -185,6 +220,7 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs53l32a", .driverid = I2C_DRIVERID_CS53L32A, .command = cs53l32a_command, + .remove = cs53l32a_remove, .probe = cs53l32a_probe, .id_table = cs53l32a_id, }; |