diff options
Diffstat (limited to 'linux')
40 files changed, 4027 insertions, 2611 deletions
diff --git a/linux/Documentation/video4linux/v4l2-framework.txt b/linux/Documentation/video4linux/v4l2-framework.txt new file mode 100644 index 000000000..60eaf54e7 --- /dev/null +++ b/linux/Documentation/video4linux/v4l2-framework.txt @@ -0,0 +1,362 @@ +Overview of the V4L2 driver framework +===================================== + +This text documents the various structures provided by the V4L2 framework and +their relationships. + + +Introduction +------------ + +The V4L2 drivers tend to be very complex due to the complexity of the +hardware: most devices have multiple ICs, export multiple device nodes in +/dev, and create also non-V4L2 devices such as DVB, ALSA, FB, I2C and input +(IR) devices. + +Especially the fact that V4L2 drivers have to setup supporting ICs to +do audio/video muxing/encoding/decoding makes it more complex than most. +Usually these ICs are connected to the main bridge driver through one or +more I2C busses, but other busses can also be used. Such devices are +called 'sub-devices'. + +For a long time the framework was limited to the video_device struct for +creating V4L device nodes and video_buf for handling the video buffers +(note that this document does not discuss the video_buf framework). + +This meant that all drivers had to do the setup of device instances and +connecting to sub-devices themselves. Some of this is quite complicated +to do right and many drivers never did do it correctly. + +There is also a lot of common code that could never be refactored due to +the lack of a framework. + +So this framework sets up the basic building blocks that all drivers +need and this same framework should make it much easier to refactor +common code into utility functions shared by all drivers. + + +Structure of a driver +--------------------- + +All drivers have the following structure: + +1) A struct for each device instance containing the device state. + +2) A way of initializing and commanding sub-devices (if any). + +3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and + /dev/vtxX) and keeping track of device-node specific data. + +4) Filehandle-specific structs containing per-filehandle data. + +This is a rough schematic of how it all relates: + + device instances + | + +-sub-device instances + | + \-V4L2 device nodes + | + \-filehandle instances + + +Structure of the framework +-------------------------- + +The framework closely resembles the driver structure: it has a v4l2_device +struct for the device instance data, a v4l2_subdev struct to refer to +sub-device instances, the video_device struct stores V4L2 device node data +and in the future a v4l2_fh struct will keep track of filehandle instances +(this is not yet implemented). + + +struct v4l2_device +------------------ + +Each device instance is represented by a struct v4l2_device (v4l2-device.h). +Very simple devices can just allocate this struct, but most of the time you +would embed this struct inside a larger struct. + +You must register the device instance: + + v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); + +Registration will initialize the v4l2_device struct and link dev->driver_data +to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from +dev (driver name followed by the bus_id, to be precise). You may change the +name after registration if you want. + +You unregister with: + + v4l2_device_unregister(struct v4l2_device *v4l2_dev); + +Unregistering will also automatically unregister all subdevs from the device. + +Sometimes you need to iterate over all devices registered by a specific +driver. This is usually the case if multiple device drivers use the same +hardware. E.g. the ivtvfb driver is a framebuffer driver that uses the ivtv +hardware. The same is true for alsa drivers for example. + +You can iterate over all registered devices as follows: + +static int callback(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + + /* test if this device was inited */ + if (v4l2_dev == NULL) + return 0; + ... + return 0; +} + +int iterate(void *p) +{ + struct device_driver *drv; + int err; + + /* Find driver 'ivtv' on the PCI bus. + pci_bus_type is a global. For USB busses use usb_bus_type. */ + drv = driver_find("ivtv", &pci_bus_type); + /* iterate over all ivtv device instances */ + err = driver_for_each_device(drv, NULL, p, callback); + put_driver(drv); + return err; +} + +Sometimes you need to keep a running counter of the device instance. This is +commonly used to map a device instance to an index of a module option array. + +The recommended approach is as follows: + +static atomic_t drv_instance = ATOMIC_INIT(0); + +static int __devinit drv_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + ... + state->instance = atomic_inc_return(&drv_instance) - 1; +} + + +struct v4l2_subdev +------------------ + +Many drivers need to communicate with sub-devices. These devices can do all +sort of tasks, but most commonly they handle audio and/or video muxing, +encoding or decoding. For webcams common sub-devices are sensors and camera +controllers. + +Usually these are I2C devices, but not necessarily. In order to provide the +driver with a consistent interface to these sub-devices the v4l2_subdev struct +(v4l2-subdev.h) was created. + +Each sub-device driver must have a v4l2_subdev struct. This struct can be +stand-alone for simple sub-devices or it might be embedded in a larger struct +if more state information needs to be stored. Usually there is a low-level +device struct (e.g. i2c_client) that contains the device data as setup +by the kernel. It is recommended to store that pointer in the private +data of v4l2_subdev using v4l2_set_subdevdata(). That makes it easy to go +from a v4l2_subdev to the actual low-level bus-specific device data. + +You also need a way to go from the low-level struct to v4l2_subdev. For the +common i2c_client struct the i2c_set_clientdata() call is used to store a +v4l2_subdev pointer, for other busses you may have to use other methods. + +From the bridge driver perspective you load the sub-device module and somehow +obtain the v4l2_subdev pointer. For i2c devices this is easy: you call +i2c_get_clientdata(). For other busses something similar needs to be done. +Helper functions exists for sub-devices on an I2C bus that do most of this +tricky work for you. + +Each v4l2_subdev contains function pointers that sub-device drivers can +implement (or leave NULL if it is not applicable). Since sub-devices can do +so many different things and you do not want to end up with a huge ops struct +of which only a handful of ops are commonly implemented, the function pointers +are sorted according to category and each category has its own ops struct. + +The top-level ops struct contains pointers to the category ops structs, which +may be NULL if the subdev driver does not support anything from that category. + +It looks like this: + +struct v4l2_subdev_core_ops { + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*log_status)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd, u32 val); + ... +}; + +struct v4l2_subdev_tuner_ops { + ... +}; + +struct v4l2_subdev_audio_ops { + ... +}; + +struct v4l2_subdev_video_ops { + ... +}; + +struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; +}; + +The core ops are common to all subdevs, the other categories are implemented +depending on the sub-device. E.g. a video device is unlikely to support the +audio ops and vice versa. + +This setup limits the number of function pointers while still making it easy +to add new ops and categories. + +A sub-device driver initializes the v4l2_subdev struct using: + + v4l2_subdev_init(subdev, &ops); + +Afterwards you need to initialize subdev->name with a unique name and set the +module owner. This is done for you if you use the i2c helper functions. + +A device (bridge) driver needs to register the v4l2_subdev with the +v4l2_device: + + int err = v4l2_device_register_subdev(device, subdev); + +This can fail if the subdev module disappeared before it could be registered. +After this function was called successfully the subdev->dev field points to +the v4l2_device. + +You can unregister a sub-device using: + + v4l2_device_unregister_subdev(subdev); + +Afterwards the subdev module can be unloaded and subdev->dev == NULL. + +You can call an ops function either directly: + + err = subdev->ops->core->g_chip_ident(subdev, &chip); + +but it is better and easier to use this macro: + + err = v4l2_subdev_call(subdev, core, g_chip_ident, &chip); + +The macro will to the right NULL pointer checks and returns -ENODEV if subdev +is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is +NULL, or the actual result of the subdev->ops->core->g_chip_ident ops. + +It is also possible to call all or a subset of the sub-devices: + + v4l2_device_call_all(dev, 0, core, g_chip_ident, &chip); + +Any subdev that does not support this ops is skipped and error results are +ignored. If you want to check for errors use this: + + err = v4l2_device_call_until_err(dev, 0, core, g_chip_ident, &chip); + +Any error except -ENOIOCTLCMD will exit the loop with that error. If no +errors (except -ENOIOCTLCMD) occured, then 0 is returned. + +The second argument to both calls is a group ID. If 0, then all subdevs are +called. If non-zero, then only those whose group ID match that value will +be called. Before a bridge driver registers a subdev it can set subdev->grp_id +to whatever value it wants (it's 0 by default). This value is owned by the +bridge driver and the sub-device driver will never modify or use it. + +The group ID gives the bridge driver more control how callbacks are called. +For example, there may be multiple audio chips on a board, each capable of +changing the volume. But usually only one will actually be used when the +user want to change the volume. You can set the group ID for that subdev to +e.g. AUDIO_CONTROLLER and specify that as the group ID value when calling +v4l2_device_call_all(). That ensures that it will only go to the subdev +that needs it. + +The advantage of using v4l2_subdev is that it is a generic struct and does +not contain any knowledge about the underlying hardware. So a driver might +contain several subdevs that use an I2C bus, but also a subdev that is +controlled through GPIO pins. This distinction is only relevant when setting +up the device, but once the subdev is registered it is completely transparent. + + +I2C sub-device drivers +---------------------- + +Since these drivers are so common, special helper functions are available to +ease the use of these drivers (v4l2-common.h). + +The recommended method of adding v4l2_subdev support to an I2C driver is to +embed the v4l2_subdev struct into the state struct that is created for each +I2C device instance. Very simple devices have no state struct and in that case +you can just create a v4l2_subdev directly. + +A typical state struct would look like this (where 'chipname' is replaced by +the name of the chip): + +struct chipname_state { + struct v4l2_subdev sd; + ... /* additional state fields */ +}; + +Initialize the v4l2_subdev struct as follows: + + v4l2_i2c_subdev_init(&state->sd, client, subdev_ops); + +This function will fill in all the fields of v4l2_subdev and ensure that the +v4l2_subdev and i2c_client both point to one another. + +You should also add a helper inline function to go from a v4l2_subdev pointer +to a chipname_state struct: + +static inline struct chipname_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct chipname_state, sd); +} + +Use this to go from the v4l2_subdev struct to the i2c_client struct: + + struct i2c_client *client = v4l2_get_subdevdata(sd); + +And this to go from an i2c_client to a v4l2_subdev struct: + + struct v4l2_subdev *sd = i2c_get_clientdata(client); + +Finally you need to make a command function to make driver->command() +call the right subdev_ops functions: + +static int subdev_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +If driver->command is never used then you can leave this out. Eventually the +driver->command usage should be removed from v4l. + +Make sure to call v4l2_device_unregister_subdev(sd) when the remove() callback +is called. This will unregister the sub-device from the bridge driver. It is +safe to call this even if the sub-device was never registered. + + +The bridge driver also has some helper functions it can use: + +struct v4l2_subdev *sd = v4l2_i2c_new_subdev(adapter, "module_foo", "chipid", 0x36); + +This loads the given module (can be NULL if no module needs to be loaded) and +calls i2c_new_device() with the given i2c_adapter and chip/address arguments. +If all goes well, then it registers the subdev with the v4l2_device. It gets +the v4l2_device by calling i2c_get_adapdata(adapter), so you should make sure +that adapdata is set to v4l2_device when you setup the i2c_adapter in your +driver. + +You can also use v4l2_i2c_new_probed_subdev() which is very similar to +v4l2_i2c_new_subdev(), except that it has an array of possible I2C addresses +that it should probe. Internally it calls i2c_new_probed_device(). + +Both functions return NULL if something went wrong. + + +struct video_device +------------------- + +Not yet documented. diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 492ab3dce..84a2be0cb 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -10,7 +10,7 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-compat-ioctl32.o v4l2-int-device.o diff --git a/linux/drivers/media/video/cs53l32a.c b/linux/drivers/media/video/cs53l32a.c index 68f31270c..0620c4af4 100644 --- a/linux/drivers/media/video/cs53l32a.c +++ b/linux/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> #include "compat.h" @@ -48,84 +48,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 */ @@ -138,6 +158,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 */ @@ -154,32 +175,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; +} + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) static const struct i2c_device_id cs53l32a_id[] = { { "cs53l32a", 0 }, @@ -192,6 +227,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, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = cs53l32a_id, diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c index 8e134312c..3ee7d7a7d 100644 --- a/linux/drivers/media/video/cx25840/cx25840-audio.c +++ b/linux/drivers/media/video/cx25840/cx25840-audio.c @@ -26,7 +26,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); if (freq != 32000 && freq != 44100 && freq != 48000) return -EINVAL; @@ -194,7 +194,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) void cx25840_audio_set_path(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); /* assert soft reset */ cx25840_and_or(client, 0x810, ~0x1, 0x01); @@ -236,7 +236,7 @@ void cx25840_audio_set_path(struct i2c_client *client) static int get_volume(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; if (state->unmute_volume >= 0) @@ -253,7 +253,7 @@ static int get_volume(struct i2c_client *client) static void set_volume(struct i2c_client *client, int volume) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); int vol; if (state->unmute_volume >= 0) { @@ -341,14 +341,14 @@ static void set_balance(struct i2c_client *client, int balance) static int get_mute(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); return state->unmute_volume >= 0; } static void set_mute(struct i2c_client *client, int mute) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); if (mute && state->unmute_volume == -1) { int vol = get_volume(client); @@ -366,7 +366,7 @@ static void set_mute(struct i2c_client *client, int mute) int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct v4l2_control *ctrl = arg; int retval; diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index cb4a9c64e..52a2a5799 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -198,7 +198,7 @@ void cx25840_work_handler(void *arg) static void cx25840_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; /* datasheet startup in numbered steps, refer to page 3-77 */ @@ -270,7 +270,7 @@ static void cx25840_initialize(struct i2c_client *client) static void cx23885_initialize(struct i2c_client *client) { DEFINE_WAIT(wait); - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; /* Internal Reset */ @@ -369,7 +369,7 @@ static void cx23885_initialize(struct i2c_client *client) void cx25840_std_setup(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); v4l2_std_id std = state->std; int hblank, hactive, burst, vblank, vactive, sc; int vblank656, src_decimation; @@ -516,7 +516,7 @@ void cx25840_std_setup(struct i2c_client *client) static void input_change(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); v4l2_std_id std = state->std; /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ @@ -570,7 +570,7 @@ static void input_change(struct i2c_client *client) static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, enum cx25840_audio_input aud_input) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8); u8 reg; @@ -690,7 +690,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp static int set_v4lstd(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 fmt = 0; /* zero is autodetect */ u8 pal_m = 0; @@ -739,9 +739,10 @@ static int set_v4lstd(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: @@ -805,9 +806,10 @@ static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: @@ -842,21 +844,23 @@ static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) /* ----------------------------------------------------------------------- */ -static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: return cx25840_vbi(client, VIDIOC_G_FMT, fmt); default: return -EINVAL; } - return 0; } -static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); struct v4l2_pix_format *pix; int HSC, VSC, Vsrc, Hsrc, filter, Vlines; int is_50Hz = !(state->std & V4L2_STD_525_60); @@ -933,7 +937,7 @@ static void log_video_status(struct i2c_client *client) "0xD", "0xE", "0xF" }; - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; u8 gen_stat1 = cx25840_read(client, 0x40d); u8 gen_stat2 = cx25840_read(client, 0x40e); @@ -963,7 +967,7 @@ static void log_video_status(struct i2c_client *client) static void log_audio_status(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 download_ctl = cx25840_read(client, 0x803); u8 mod_det_stat0 = cx25840_read(client, 0x804); u8 mod_det_stat1 = cx25840_read(client, 0x805); @@ -1116,21 +1120,12 @@ static void log_audio_status(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -static int cx25840_command(struct i2c_client *client, unsigned int cmd, - void *arg) +static int cx25840_init(struct v4l2_subdev *sd, u32 val) { - struct cx25840_state *state = i2c_get_clientdata(client); - struct v4l2_tuner *vt = arg; - struct v4l2_routing *route = arg; - - /* ignore these commands */ - switch (cmd) { - case TUNER_SET_TYPE_ADDR: - return 0; - } + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); if (!state->is_initialized) { - v4l_dbg(1, cx25840_debug, client, "cmd %08x triggered fw load\n", cmd); /* initialize on first use */ state->is_initialized = 1; if (state->is_cx25836) @@ -1140,50 +1135,69 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, else cx25840_initialize(client); } + return 0; +} - switch (cmd) { #ifdef CONFIG_VIDEO_ADV_DEBUG - /* ioctls to allow direct access to the - * cx25840 registers for testing */ - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; +static int cx25840_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = cx25840_read(client, reg->reg & 0x0fff); - else - cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); - break; - } + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = cx25840_read(client, reg->reg & 0x0fff); + return 0; +} + +static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); + return 0; +} #endif - case VIDIOC_INT_DECODE_VBI_LINE: - return cx25840_vbi(client, cmd, arg); +static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return cx25840_audio(client, cmd, arg); + return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi); +} + +static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq); +} + +static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_STREAMON: - v4l_dbg(1, cx25840_debug, client, "enable output\n"); + v4l_dbg(1, cx25840_debug, client, "%s output\n", + enable ? "enable" : "disable"); + if (enable) { if (state->is_cx23885) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { cx25840_write(client, 0x115, - state->is_cx25836 ? 0x0c : 0x8c); + state->is_cx25836 ? 0x0c : 0x8c); cx25840_write(client, 0x116, - state->is_cx25836 ? 0x04 : 0x07); + state->is_cx25836 ? 0x04 : 0x07); } - break; - - case VIDIOC_STREAMOFF: - v4l_dbg(1, cx25840_debug, client, "disable output\n"); + } else { if (state->is_cx23885) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); @@ -1191,133 +1205,136 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, cx25840_write(client, 0x115, 0x00); cx25840_write(client, 0x116, 0x00); } - break; + } + return 0; +} - case VIDIOC_LOG_STATUS: - log_video_status(client); - if (!state->is_cx25836) - log_audio_status(client); - break; +static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct cx25840_state *state = to_state(sd); - case VIDIOC_G_CTRL: - return get_v4lctrl(client, (struct v4l2_control *)arg); + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + break; + } + if (state->is_cx25836) + return -EINVAL; - case VIDIOC_S_CTRL: - return set_v4lctrl(client, (struct v4l2_control *)arg); + switch (qc->id) { + case V4L2_CID_AUDIO_VOLUME: + return v4l2_ctrl_query_fill(qc, 0, 65535, + 65535 / 100, state->default_volume); + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; + } + return -EINVAL; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int cx25840_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - break; - } - if (state->is_cx25836) - return -EINVAL; + if (state->radio == 0 && state->std == std) + return 0; + state->radio = 0; + state->std = std; + return set_v4lstd(client); +} - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 65535, - 65535 / 100, state->default_volume); - case V4L2_CID_AUDIO_MUTE: - case V4L2_CID_AUDIO_BALANCE: - case V4L2_CID_AUDIO_BASS: - case V4L2_CID_AUDIO_TREBLE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } - return -EINVAL; - } +static int cx25840_s_radio(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = state->std; - break; + state->radio = 1; + return 0; +} - case VIDIOC_S_STD: - if (state->radio == 0 && state->std == *(v4l2_std_id *)arg) - return 0; - state->radio = 0; - state->std = *(v4l2_std_id *)arg; - return set_v4lstd(client); +static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case AUDC_SET_RADIO: - state->radio = 1; - break; + return set_input(client, route->input, state->aud_input); +} - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->vid_input; - route->output = 0; - break; +static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_VIDEO_ROUTING: - return set_input(client, route->input, state->aud_input); + if (state->is_cx25836) + return -EINVAL; + return set_input(client, state->vid_input, route->input); +} - case VIDIOC_INT_G_AUDIO_ROUTING: - if (state->is_cx25836) - return -EINVAL; - route->input = state->aud_input; - route->output = 0; - break; +static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_AUDIO_ROUTING: - if (state->is_cx25836) - return -EINVAL; - return set_input(client, state->vid_input, route->input); + if (!state->is_cx25836) + input_change(client); + return 0; +} - case VIDIOC_S_FREQUENCY: - if (!state->is_cx25836) { - input_change(client); - } - break; +static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 vpres = cx25840_read(client, 0x40e) & 0x20; + u8 mode; + int val = 0; - case VIDIOC_G_TUNER: - { - u8 vpres = cx25840_read(client, 0x40e) & 0x20; - u8 mode; - int val = 0; + if (state->radio) + return 0; - if (state->radio) - break; + vt->signal = vpres ? 0xffff : 0x0; + if (state->is_cx25836) + return 0; - vt->signal = vpres ? 0xffff : 0x0; - if (state->is_cx25836) - break; + vt->capability |= + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; - vt->capability |= - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; + mode = cx25840_read(client, 0x804); - mode = cx25840_read(client, 0x804); + /* get rxsubchans and audmode */ + if ((mode & 0xf) == 1) + val |= V4L2_TUNER_SUB_STEREO; + else + val |= V4L2_TUNER_SUB_MONO; - /* get rxsubchans and audmode */ - if ((mode & 0xf) == 1) - val |= V4L2_TUNER_SUB_STEREO; - else - val |= V4L2_TUNER_SUB_MONO; + if (mode == 2 || mode == 4) + val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - if (mode == 2 || mode == 4) - val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + if (mode & 0x10) + val |= V4L2_TUNER_SUB_SAP; - if (mode & 0x10) - val |= V4L2_TUNER_SUB_SAP; + vt->rxsubchans = val; + vt->audmode = state->audmode; + return 0; +} - vt->rxsubchans = val; - vt->audmode = state->audmode; - break; - } +static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_TUNER: - if (state->radio || state->is_cx25836) - break; + if (state->radio || state->is_cx25836) + return 0; - switch (vt->audmode) { + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: /* mono -> mono stereo -> mono @@ -1345,41 +1362,100 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, break; default: return -EINVAL; - } - state->audmode = vt->audmode; - break; + } + state->audmode = vt->audmode; + return 0; +} - case VIDIOC_G_FMT: - return get_v4lfmt(client, (struct v4l2_format *)arg); +static int cx25840_reset(struct v4l2_subdev *sd, u32 val) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_FMT: - return set_v4lfmt(client, (struct v4l2_format *)arg); + if (state->is_cx25836) + cx25836_initialize(client); + else if (state->is_cx23885) + cx23885_initialize(client); + else + cx25840_initialize(client); + return 0; +} - case VIDIOC_INT_RESET: - if (state->is_cx25836) - cx25836_initialize(client); - else if (state->is_cx23885) - cx23885_initialize(client); - else - cx25840_initialize(client); - break; +static int cx25840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev); + return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev); +} - default: - return -EINVAL; - } +static int cx25840_log_status(struct v4l2_subdev *sd) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + log_video_status(client); + if (!state->is_cx25836) + log_audio_status(client); return 0; } +static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops cx25840_core_ops = { + .log_status = cx25840_log_status, + .g_chip_ident = cx25840_g_chip_ident, + .g_ctrl = cx25840_g_ctrl, + .s_ctrl = cx25840_s_ctrl, + .queryctrl = cx25840_queryctrl, + .reset = cx25840_reset, + .init = cx25840_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx25840_g_register, + .s_register = cx25840_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { + .s_frequency = cx25840_s_frequency, + .s_std = cx25840_s_std, + .s_radio = cx25840_s_radio, + .g_tuner = cx25840_g_tuner, + .s_tuner = cx25840_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { + .s_clock_freq = cx25840_s_clock_freq, + .s_routing = cx25840_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops cx25840_video_ops = { + .s_routing = cx25840_s_video_routing, + .g_fmt = cx25840_g_fmt, + .s_fmt = cx25840_s_fmt, + .decode_vbi_line = cx25840_decode_vbi_line, + .s_stream = cx25840_s_stream, +}; + +static const struct v4l2_subdev_ops cx25840_ops = { + .core = &cx25840_core_ops, + .tuner = &cx25840_tuner_ops, + .audio = &cx25840_audio_ops, + .video = &cx25840_video_ops, +}; + /* ----------------------------------------------------------------------- */ static int cx25840_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct cx25840_state *state; + struct v4l2_subdev *sd; u32 id; u16 device_id; @@ -1411,10 +1487,11 @@ static int cx25840_probe(struct i2c_client *client, } state = kzalloc(sizeof(struct cx25840_state), GFP_KERNEL); - if (state == NULL) { + if (state == NULL) return -ENOMEM; - } + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &cx25840_ops); /* Note: revision '(device_id & 0x0f) == 2' was never built. The marking skips from 0x1 == 22 to 0x3 == 23. */ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", @@ -1422,7 +1499,6 @@ static int cx25840_probe(struct i2c_client *client, (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), client->addr << 1, client->adapter->name); - i2c_set_clientdata(client, state); state->c = client; state->is_cx25836 = ((device_id & 0xff00) == 0x8300); state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); @@ -1449,7 +1525,10 @@ static int cx25840_probe(struct i2c_client *client, static int cx25840_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/cx25840/cx25840-core.h b/linux/drivers/media/video/cx25840/cx25840-core.h index cd549b119..0a99c19df 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.h +++ b/linux/drivers/media/video/cx25840/cx25840-core.h @@ -23,6 +23,7 @@ #include "compat.h" #include <linux/videodev2.h> +#include <media/v4l2-device.h> #include <linux/i2c.h> /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is @@ -35,6 +36,7 @@ struct cx25840_state { struct i2c_client *c; + struct v4l2_subdev sd; int pvr150_workaround; int radio; v4l2_std_id std; @@ -54,6 +56,11 @@ struct cx25840_state { struct work_struct fw_work; /* work entry for fw load */ }; +static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct cx25840_state, sd); +} + /* ----------------------------------------------------------------------- */ /* cx25850-core.c */ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index 04a5dbb65..01fbe174e 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -92,7 +92,7 @@ static int fw_write(struct i2c_client *client, const u8 *data, int size) int cx25840_loadfw(struct i2c_client *client) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); const struct firmware *fw = NULL; u8 buffer[FWSEND]; const u8 *ptr; diff --git a/linux/drivers/media/video/cx25840/cx25840-vbi.c b/linux/drivers/media/video/cx25840/cx25840-vbi.c index e45c39625..7790afd5b 100644 --- a/linux/drivers/media/video/cx25840/cx25840-vbi.c +++ b/linux/drivers/media/video/cx25840/cx25840-vbi.c @@ -85,7 +85,7 @@ static int decode_vps(u8 * dst, u8 * p) int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) { - struct cx25840_state *state = i2c_get_clientdata(client); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct v4l2_format *fmt; struct v4l2_sliced_vbi_format *svbi; diff --git a/linux/drivers/media/video/ivtv/ivtv-controls.c b/linux/drivers/media/video/ivtv/ivtv-controls.c index 48e103be7..62aa06f5d 100644 --- a/linux/drivers/media/video/ivtv/ivtv-controls.c +++ b/linux/drivers/media/video/ivtv/ivtv-controls.c @@ -63,7 +63,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -73,7 +73,7 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl)) qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0; @@ -122,7 +122,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -130,7 +130,7 @@ static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl); default: IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); @@ -147,7 +147,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_HUE: case V4L2_CID_SATURATION: case V4L2_CID_CONTRAST: - return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl); case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: @@ -155,7 +155,7 @@ static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_LOUDNESS: - return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl); default: IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); return -EINVAL; @@ -268,7 +268,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); fmt.fmt.pix.height = itv->params.height; - itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, &fmt); } err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) @@ -279,7 +279,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ if (idx < sizeof(freqs)) - ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[idx]); + ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); return err; } return -EINVAL; diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index db866d912..b29625ca2 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -60,9 +60,6 @@ #include <media/v4l2-chip-ident.h> #include "tuner-xc2028.h" -/* var to keep track of the number of array elements in use */ -int ivtv_cards_active; - /* If you have already X v4l cards, then set this to X. This way the device numbers stay matched. Example: you have a WinTV card without radio and a PVR-350 with. Normally this would give a @@ -70,12 +67,6 @@ int ivtv_cards_active; setting this to 1 you ensure that radio0 is now also radio1. */ int ivtv_first_minor; -/* Master variable for all ivtv info */ -struct ivtv *ivtv_cards[IVTV_MAX_CARDS]; - -/* Protects ivtv_cards_active */ -DEFINE_SPINLOCK(ivtv_cards_lock); - /* add your revision and whatnot here */ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, @@ -87,6 +78,9 @@ static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); +/* ivtv instance counter */ +static atomic_t ivtv_instance = ATOMIC_INIT(0); + /* Parameter declarations */ static int cardtype[IVTV_MAX_CARDS]; static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, @@ -599,9 +593,9 @@ static void ivtv_process_options(struct ivtv *itv) itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers * 1024; itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers * 1024; itv->options.kilobytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers; - itv->options.cardtype = cardtype[itv->num]; - itv->options.tuner = tuner[itv->num]; - itv->options.radio = radio[itv->num]; + itv->options.cardtype = cardtype[itv->instance]; + itv->options.tuner = tuner[itv->instance]; + itv->options.radio = radio[itv->instance]; itv->options.newi2c = newi2c; if (tunertype < -1 || tunertype > 1) { IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); @@ -688,7 +682,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) spin_lock_init(&itv->lock); spin_lock_init(&itv->dma_reg_lock); - itv->irq_work_queues = create_singlethread_workqueue(itv->name); + itv->irq_work_queues = create_singlethread_workqueue(itv->device.name); if (itv->irq_work_queues == NULL) { IVTV_ERR("Could not create ivtv workqueue\n"); return -1; @@ -774,12 +768,6 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv) i = 0; itv->active_input = i; itv->audio_input = itv->card->video_inputs[i].audio_index; - if (itv->card->hw_all & IVTV_HW_CX25840) - itv->video_dec_func = ivtv_cx25840; - else if (itv->card->hw_all & IVTV_HW_SAA717X) - itv->video_dec_func = ivtv_saa717x; - else - itv->video_dec_func = ivtv_saa7115; } static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, @@ -792,21 +780,21 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, IVTV_DEBUG_INFO("Enabling pci device\n"); if (pci_enable_device(dev)) { - IVTV_ERR("Can't enable device %d!\n", itv->num); + IVTV_ERR("Can't enable device!\n"); return -EIO; } if (pci_set_dma_mask(dev, 0xffffffff)) { - IVTV_ERR("No suitable DMA available on card %d.\n", itv->num); + IVTV_ERR("No suitable DMA available.\n"); return -EIO; } if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { - IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request encoder memory region.\n"); return -EIO; } if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE, "ivtv registers")) { - IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request register memory region.\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); return -EIO; } @@ -814,7 +802,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, if (itv->has_cx23415 && !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE, "ivtv decoder")) { - IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num); + IVTV_ERR("Cannot request decoder memory region.\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); return -EIO; @@ -857,69 +845,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, return 0; } -#ifdef MODULE -static u32 ivtv_request_module(struct ivtv *itv, u32 hw, - const char *name, u32 id) -{ - if ((hw & id) == 0) - return hw; - if (request_module(name) != 0) { - IVTV_ERR("Failed to load module %s\n", name); - return hw & ~id; - } - IVTV_DEBUG_INFO("Loaded module %s\n", name); - return hw; -} -#endif - static void ivtv_load_and_init_modules(struct ivtv *itv) { u32 hw = itv->card->hw_all; unsigned i; -#ifdef MODULE - /* load modules */ -#ifdef CONFIG_MEDIA_TUNER_MODULE - hw = ivtv_request_module(itv, hw, "tuner", IVTV_HW_TUNER); -#endif -#ifdef CONFIG_VIDEO_CX25840_MODULE - hw = ivtv_request_module(itv, hw, "cx25840", IVTV_HW_CX25840); -#endif -#ifdef CONFIG_VIDEO_SAA711X_MODULE - hw = ivtv_request_module(itv, hw, "saa7115", IVTV_HW_SAA711X); -#endif -#ifdef CONFIG_VIDEO_SAA7127_MODULE - hw = ivtv_request_module(itv, hw, "saa7127", IVTV_HW_SAA7127); -#endif -#ifdef CONFIG_VIDEO_SAA717X_MODULE - hw = ivtv_request_module(itv, hw, "saa717x", IVTV_HW_SAA717X); -#endif -#ifdef CONFIG_VIDEO_UPD64031A_MODULE - hw = ivtv_request_module(itv, hw, "upd64031a", IVTV_HW_UPD64031A); -#endif -#ifdef CONFIG_VIDEO_UPD64083_MODULE - hw = ivtv_request_module(itv, hw, "upd64083", IVTV_HW_UPD6408X); -#endif -#ifdef CONFIG_VIDEO_MSP3400_MODULE - hw = ivtv_request_module(itv, hw, "msp3400", IVTV_HW_MSP34XX); -#endif -#ifdef CONFIG_VIDEO_VP27SMPX_MODULE - hw = ivtv_request_module(itv, hw, "vp27smpx", IVTV_HW_VP27SMPX); -#endif -#ifdef CONFIG_VIDEO_WM8775_MODULE - hw = ivtv_request_module(itv, hw, "wm8775", IVTV_HW_WM8775); -#endif -#ifdef CONFIG_VIDEO_WM8739_MODULE - hw = ivtv_request_module(itv, hw, "wm8739", IVTV_HW_WM8739); -#endif -#ifdef CONFIG_VIDEO_CS53L32A_MODULE - hw = ivtv_request_module(itv, hw, "cs53l32a", IVTV_HW_CS53L32A); -#endif -#ifdef CONFIG_VIDEO_M52790_MODULE - hw = ivtv_request_module(itv, hw, "m52790", IVTV_HW_M52790); -#endif -#endif - /* check which i2c devices are actually found */ for (i = 0; i < 32; i++) { u32 device = 1 << i; @@ -931,11 +861,21 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) itv->hw_flags |= device; continue; } - ivtv_i2c_register(itv, i); - if (ivtv_i2c_hw_addr(itv, device) > 0) + if (ivtv_i2c_register(itv, i) == 0) itv->hw_flags |= device; } + if (itv->card->hw_all & IVTV_HW_CX25840) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840); + else if (itv->card->hw_all & IVTV_HW_SAA717X) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA717X); + else if (itv->card->hw_all & IVTV_HW_SAA7114) + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7114); + else + itv->sd_video = ivtv_find_hw(itv, IVTV_HW_SAA7115); + itv->sd_audio = ivtv_find_hw(itv, itv->card->hw_audio_ctrl); + itv->sd_muxer = ivtv_find_hw(itv, itv->card->hw_muxer); + hw = itv->hw_flags; if (itv->card->type == IVTV_CARD_CX23416GYC) { @@ -953,7 +893,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) /* The crystal frequency of GVMVPRX is 24.576MHz */ crystal_freq.freq = SAA7115_FREQ_24_576_MHZ; crystal_freq.flags = SAA7115_FREQ_FL_UCGC; - itv->video_dec_func(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, &crystal_freq); } if (hw & IVTV_HW_CX25840) { @@ -971,7 +911,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) /* determine the exact saa711x model */ itv->hw_flags &= ~IVTV_HW_SAA711X; - ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v); + ivtv_call_hw(itv, IVTV_HW_SAA711X, core, g_chip_ident, &v); if (v.ident == V4L2_IDENT_SAA7114) { itv->hw_flags |= IVTV_HW_SAA7114; /* VBI is not yet supported by the saa7114 driver. */ @@ -1005,28 +945,20 @@ static int __devinit ivtv_probe(struct pci_dev *dev, int vbi_buf_size; struct ivtv *itv; - spin_lock(&ivtv_cards_lock); - - /* Make sure we've got a place for this card */ - if (ivtv_cards_active == IVTV_MAX_CARDS) { - printk(KERN_ERR "ivtv: Maximum number of cards detected (%d)\n", - ivtv_cards_active); - spin_unlock(&ivtv_cards_lock); - return -ENOMEM; - } - itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); - if (itv == NULL) { - spin_unlock(&ivtv_cards_lock); + if (itv == NULL) return -ENOMEM; - } - ivtv_cards[ivtv_cards_active] = itv; itv->dev = dev; - itv->num = ivtv_cards_active++; - snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num); - IVTV_INFO("Initializing card #%d\n", itv->num); + itv->instance = atomic_inc_return(&ivtv_instance) - 1; - spin_unlock(&ivtv_cards_lock); + retval = v4l2_device_register(&dev->dev, &itv->device); + if (retval) + return retval; + /* "ivtv + PCI ID" is a bit of a mouthful, so use + "ivtv + instance" instead. */ + snprintf(itv->device.name, sizeof(itv->device.name), + "ivtv%d", itv->instance); + IVTV_INFO("Initializing card %d\n", itv->instance); ivtv_process_options(itv); if (itv->options.cardtype == -1) { @@ -1047,8 +979,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, else if (retval == -ENXIO) goto free_mem; } - /* save itv in the pci struct for later use */ - pci_set_drvdata(dev, itv); /* map io memory */ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", @@ -1090,7 +1020,9 @@ static int __devinit ivtv_probe(struct pci_dev *dev, goto free_io; } - ivtv_gpio_init(itv); + retval = ivtv_gpio_init(itv); + if (retval) + goto free_io; /* active i2c */ IVTV_DEBUG_INFO("activating i2c...\n"); @@ -1099,8 +1031,6 @@ static int __devinit ivtv_probe(struct pci_dev *dev, goto free_io; } - IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active); - if (itv->card->hw_all & IVTV_HW_TVEEPROM) { /* Based on the model number the cardtype may be changed. The PCI IDs are not always reliable. */ @@ -1195,7 +1125,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ setup.tuner_callback = (setup.type == TUNER_XC2028) ? ivtv_reset_tuner_gpio : NULL; - ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup); + ivtv_call_all(itv, tuner, s_type_addr, &setup); if (setup.type == TUNER_XC2028) { static struct xc2028_ctrl ctrl = { .fname = XC2028_DEFAULT_FIRMWARE, @@ -1205,7 +1135,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, .tuner = itv->options.tuner, .priv = &ctrl, }; - ivtv_call_i2c_clients(itv, TUNER_SET_CONFIG, &cfg); + ivtv_call_all(itv, tuner, s_config, &cfg); } } @@ -1214,11 +1144,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev, itv->tuner_std = itv->std; if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { - ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std); + ivtv_call_all(itv, video, s_std_output, itv->std); /* Turn off the output signal. The mpeg decoder is not yet active so without this you would get a green image until the mpeg decoder becomes active. */ - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); } /* clear interrupt mask, effectively disabling interrupts */ @@ -1226,7 +1156,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, /* Register IRQ */ retval = request_irq(itv->dev->irq, ivtv_irq_handler, - IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv); + IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv); if (retval) { IVTV_ERR("Failed to register irq %d\n", retval); goto free_i2c; @@ -1242,7 +1172,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev, IVTV_ERR("Error %d registering devices\n", retval); goto free_streams; } - IVTV_INFO("Initialized card #%d: %s\n", itv->num, itv->card_name); + IVTV_INFO("Initialized card: %s\n", itv->card_name); return 0; free_streams: @@ -1265,10 +1195,8 @@ err: retval = -ENODEV; IVTV_ERR("Error %d on initialization\n", retval); - spin_lock(&ivtv_cards_lock); - kfree(ivtv_cards[ivtv_cards_active]); - ivtv_cards[ivtv_cards_active] = NULL; - spin_unlock(&ivtv_cards_lock); + v4l2_device_unregister(&itv->device); + kfree(itv); return retval; } @@ -1308,10 +1236,11 @@ int ivtv_init_on_first_open(struct ivtv *itv) if (itv->card->hw_all & IVTV_HW_CX25840) { struct v4l2_control ctrl; + v4l2_subdev_call(itv->sd_video, core, init, 0); /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ ctrl.id = V4L2_CID_PRIVATE_BASE; ctrl.value = itv->pvr150_workaround; - itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); + v4l2_subdev_call(itv->sd_video, core, s_ctrl, &ctrl); } vf.tuner = 0; @@ -1341,7 +1270,7 @@ int ivtv_init_on_first_open(struct ivtv *itv) /* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes the mpeg decoder so now the saa7127 receives a proper signal. */ - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); ivtv_init_mpeg_decoder(itv); } ivtv_s_std(NULL, &fh, &itv->tuner_std); @@ -1366,9 +1295,11 @@ int ivtv_init_on_first_open(struct ivtv *itv) static void ivtv_remove(struct pci_dev *pci_dev) { - struct ivtv *itv = pci_get_drvdata(pci_dev); + struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev); + struct ivtv *itv = to_ivtv(dev); + int i; - IVTV_DEBUG_INFO("Removing Card #%d\n", itv->num); + IVTV_DEBUG_INFO("Removing card\n"); if (test_bit(IVTV_F_I_INITED, &itv->i_flags)) { /* Stop all captures */ @@ -1381,7 +1312,7 @@ static void ivtv_remove(struct pci_dev *pci_dev) /* Turn off the TV-out */ if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); if (atomic_read(&itv->decoding) > 0) { int type; @@ -1406,6 +1337,8 @@ static void ivtv_remove(struct pci_dev *pci_dev) ivtv_streams_cleanup(itv, 1); ivtv_udma_free(itv); + v4l2_device_unregister(&itv->device); + exit_ivtv_i2c(itv); free_irq(itv->dev->irq, (void *)itv); @@ -1417,8 +1350,11 @@ static void ivtv_remove(struct pci_dev *pci_dev) release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); pci_disable_device(itv->dev); + for (i = 0; i < IVTV_VBI_FRAMES; i++) + kfree(itv->vbi.sliced_mpeg_data[i]); - IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num); + printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name); + kfree(itv); } /* define a pci_driver for card detection */ @@ -1431,54 +1367,36 @@ static struct pci_driver ivtv_pci_driver = { static int module_start(void) { - printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); - - memset(ivtv_cards, 0, sizeof(ivtv_cards)); + printk(KERN_INFO "ivtv: Start initialization, version %s\n", IVTV_VERSION); /* Validate parameters */ if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) { - printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n", + printk(KERN_ERR "ivtv: Exiting, ivtv_first_minor must be between 0 and %d\n", IVTV_MAX_CARDS - 1); return -1; } if (ivtv_debug < 0 || ivtv_debug > 2047) { ivtv_debug = 0; - printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n"); + printk(KERN_INFO "ivtv: Debug value must be >= 0 and <= 2047\n"); } if (pci_register_driver(&ivtv_pci_driver)) { - printk(KERN_ERR "ivtv: Error detecting PCI card\n"); + printk(KERN_ERR "ivtv: Error detecting PCI card\n"); return -ENODEV; } - printk(KERN_INFO "ivtv: End initialization\n"); + printk(KERN_INFO "ivtv: End initialization\n"); return 0; } static void module_cleanup(void) { - int i, j; - pci_unregister_driver(&ivtv_pci_driver); - - spin_lock(&ivtv_cards_lock); - for (i = 0; i < ivtv_cards_active; i++) { - if (ivtv_cards[i] == NULL) - continue; - for (j = 0; j < IVTV_VBI_FRAMES; j++) { - kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]); - } - kfree(ivtv_cards[i]); - } - spin_unlock(&ivtv_cards_lock); } /* Note: These symbols are exported because they are used by the ivtvfb framebuffer module and an infrared module for the IR-blaster. */ EXPORT_SYMBOL(ivtv_set_irq_mask); -EXPORT_SYMBOL(ivtv_cards_active); -EXPORT_SYMBOL(ivtv_cards); -EXPORT_SYMBOL(ivtv_cards_lock); EXPORT_SYMBOL(ivtv_api); EXPORT_SYMBOL(ivtv_vapi); EXPORT_SYMBOL(ivtv_vapi_result); diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.h b/linux/drivers/media/video/ivtv/ivtv-driver.h index 9128df0f3..51f6e54ba 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.h +++ b/linux/drivers/media/video/ivtv/ivtv-driver.h @@ -62,6 +62,7 @@ #include <linux/dvb/audio.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <media/cx2341x.h> @@ -114,9 +115,6 @@ #define IVTV_REG_VPU (0x9058) #define IVTV_REG_APU (0xA064) -/* i2c stuff */ -#define I2C_CLIENTS_MAX 16 - /* debugging */ extern int ivtv_debug; @@ -133,12 +131,10 @@ extern int ivtv_debug; /* Flag to turn on high volume debugging */ #define IVTV_DBGFLG_HIGHVOL (1 << 10) -/* NOTE: extra space before comma in 'itv->num , ## args' is required for - gcc-2.95, otherwise it won't compile. */ #define IVTV_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtv_debug) \ - printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + v4l2_info(&itv->device, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -153,8 +149,8 @@ extern int ivtv_debug; #define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \ do { \ - if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ - printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ + v4l2_info(&itv->device, " " type ": " fmt , ##args); \ } while (0) #define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args) #define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args) @@ -168,9 +164,9 @@ extern int ivtv_debug; #define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) /* Standard kernel messages */ -#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) -#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) -#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_ERR(fmt, args...) v4l2_err(&itv->device, fmt , ## args) +#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->device, fmt , ## args) +#define IVTV_INFO(fmt, args...) v4l2_info(&itv->device, fmt , ## args) /* output modes (cx23415 only) */ #define OUT_NONE 0 @@ -597,8 +593,6 @@ struct ivtv_card; /* Struct to hold info about ivtv cards */ struct ivtv { /* General fixed card data */ - int num; /* board number, -1 during init! */ - char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */ struct pci_dev *dev; /* PCI device */ const struct ivtv_card *card; /* card information */ const char *card_name; /* full name of the card */ @@ -610,14 +604,18 @@ struct ivtv { u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* hardware description of the board */ v4l2_std_id tuner_std; /* the norm of the card's tuner (fixed) */ - /* controlling video decoder function */ - int (*video_dec_func)(struct ivtv *, unsigned int, void *); + struct v4l2_subdev *sd_video; /* controlling video decoder subdev */ + struct v4l2_subdev *sd_audio; /* controlling audio subdev */ + struct v4l2_subdev *sd_muxer; /* controlling audio muxer subdev */ u32 base_addr; /* PCI resource base address */ volatile void __iomem *enc_mem; /* pointer to mapped encoder memory */ volatile void __iomem *dec_mem; /* pointer to mapped decoder memory */ volatile void __iomem *reg_mem; /* pointer to mapped registers */ struct ivtv_options options; /* user options */ + struct v4l2_device device; + struct v4l2_subdev sd_gpio; /* GPIO sub-device */ + u16 instance; /* High-level state info */ unsigned long i_flags; /* global ivtv flags */ @@ -677,7 +675,6 @@ struct ivtv { struct i2c_adapter i2c_adap; struct i2c_algo_bit_data i2c_algo; struct i2c_client i2c_client; - struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];/* pointers to all I2C clients */ int i2c_state; /* i2c bit state */ struct mutex i2c_bus_lock; /* lock i2c bus */ @@ -723,11 +720,13 @@ struct ivtv { struct osd_info *osd_info; /* ivtvfb private OSD info */ }; +static inline struct ivtv *to_ivtv(struct v4l2_device *dev) +{ + return container_of(dev, struct ivtv, device); +} + /* Globals */ -extern struct ivtv *ivtv_cards[]; -extern int ivtv_cards_active; extern int ivtv_first_minor; -extern spinlock_t ivtv_cards_lock; /*==============Prototypes==================*/ @@ -787,4 +786,19 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv) #define write_dec_sync(val, addr) \ do { write_dec(val, addr); read_dec(addr); } while (0) +/* Call the specified callback for all subdevs matching hw (if 0, then + match them all). Ignore any errors. */ +#define ivtv_call_hw(itv, hw, o, f, args...) \ + __v4l2_device_call_subdevs(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args) + +/* Call the specified callback for all subdevs matching hw (if 0, then + match them all). If the callback returns an error other than 0 or + -ENOIOCTLCMD, then return with that error code. */ +#define ivtv_call_hw_err(itv, hw, o, f, args...) \ + __v4l2_device_call_subdevs_until_err(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args) + +#define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args) + #endif diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c index 1c404e454..5eb587592 100644 --- a/linux/drivers/media/video/ivtv/ivtv-fileops.c +++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c @@ -155,7 +155,7 @@ static void ivtv_dualwatch(struct ivtv *itv) new_stereo_mode = itv->params.audio_properties & stereo_mask; memset(&vt, 0, sizeof(vt)); - ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt); + ivtv_call_all(itv, tuner, g_tuner, &vt); if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) new_stereo_mode = dual; @@ -857,7 +857,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); /* Switch tuner to TV */ - ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + ivtv_call_all(itv, tuner, s_std, itv->std); /* Select correct audio input (i.e. TV tuner or Line in) */ ivtv_audio_set_io(itv); if (itv->hw_flags & IVTV_HW_SAA711X) @@ -865,7 +865,7 @@ int ivtv_v4l2_close(struct inode *inode, struct file *filp) struct v4l2_crystal_freq crystal_freq; crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; crystal_freq.flags = 0; - ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); } if (atomic_read(&itv->capturing) > 0) { /* Undo video mute */ @@ -952,15 +952,14 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) /* We have the radio */ ivtv_mute(itv); /* Switch tuner to radio */ - ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL); + ivtv_call_all(itv, tuner, s_radio); /* Select the correct audio input (i.e. radio tuner) */ ivtv_audio_set_io(itv); - if (itv->hw_flags & IVTV_HW_SAA711X) - { + if (itv->hw_flags & IVTV_HW_SAA711X) { struct v4l2_crystal_freq crystal_freq; crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; crystal_freq.flags = SAA7115_FREQ_FL_APLL; - ivtv_saa7115(itv, VIDIOC_INT_S_CRYSTAL_FREQ, &crystal_freq); + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); } /* Done! Unmute and continue. */ ivtv_unmute(itv); @@ -981,37 +980,18 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) int ivtv_v4l2_open(struct inode *inode, struct file *filp) { - int res, x, y = 0; + int res; struct ivtv *itv = NULL; struct ivtv_stream *s = NULL; - int minor = iminor(inode); - - /* Find which card this open was on */ - spin_lock(&ivtv_cards_lock); - for (x = 0; itv == NULL && x < ivtv_cards_active; x++) { - if (ivtv_cards[x] == NULL) - continue; - /* find out which stream this open was on */ - for (y = 0; y < IVTV_MAX_STREAMS; y++) { - s = &ivtv_cards[x]->streams[y]; - if (s->v4l2dev && s->v4l2dev->minor == minor) { - itv = ivtv_cards[x]; - break; - } - } - } - spin_unlock(&ivtv_cards_lock); + struct video_device *vdev = video_devdata(filp); - if (itv == NULL) { - /* Couldn't find a device registered - on that minor, shouldn't happen! */ - printk(KERN_WARNING "No ivtv device found on minor %d\n", minor); - return -ENXIO; - } + s = video_get_drvdata(vdev); + itv = s->itv; mutex_lock(&itv->serialize_lock); if (ivtv_init_on_first_open(itv)) { - IVTV_ERR("Failed to initialize on minor %d\n", minor); + IVTV_ERR("Failed to initialize on minor %d\n", + s->v4l2dev->minor); mutex_unlock(&itv->serialize_lock); return -ENXIO; } diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.c b/linux/drivers/media/video/ivtv/ivtv-gpio.c index 74a44844c..dc2850e87 100644 --- a/linux/drivers/media/video/ivtv/ivtv-gpio.c +++ b/linux/drivers/media/video/ivtv/ivtv-gpio.c @@ -144,22 +144,9 @@ int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value) return 0; } -void ivtv_gpio_init(struct ivtv *itv) +static inline struct ivtv *sd_to_ivtv(struct v4l2_subdev *sd) { - u16 pin = 0; - - if (itv->card->xceive_pin) - pin = 1 << itv->card->xceive_pin; - - if ((itv->card->gpio_init.direction | pin) == 0) - return; - - IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", - read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); - - /* init output data then direction */ - write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); - write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); + return container_of(sd, struct ivtv, sd_gpio); } static struct v4l2_queryctrl gpio_ctrl_mute = { @@ -173,134 +160,231 @@ static struct v4l2_queryctrl gpio_ctrl_mute = { .flags = 0, }; -int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg) +static int subdev_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct v4l2_tuner *tuner = arg; - struct v4l2_control *ctrl = arg; - struct v4l2_routing *route = arg; + struct ivtv *itv = sd_to_ivtv(sd); u16 mask, data; - switch (command) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - mask = itv->card->gpio_audio_freq.mask; - switch (*(u32 *)arg) { - case 32000: - data = itv->card->gpio_audio_freq.f32000; - break; - case 44100: - data = itv->card->gpio_audio_freq.f44100; - break; - case 48000: - default: - data = itv->card->gpio_audio_freq.f48000; - break; - } + mask = itv->card->gpio_audio_freq.mask; + switch (freq) { + case 32000: + data = itv->card->gpio_audio_freq.f32000; + break; + case 44100: + data = itv->card->gpio_audio_freq.f44100; + break; + case 48000: + default: + data = itv->card->gpio_audio_freq.f48000; break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_G_TUNER: - mask = itv->card->gpio_audio_detect.mask; - if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) - tuner->rxsubchans = V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; - else - tuner->rxsubchans = V4L2_TUNER_SUB_MONO; - return 0; +static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask; + + mask = itv->card->gpio_audio_detect.mask; + if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) + vt->rxsubchans = V4L2_TUNER_MODE_STEREO | + V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + else + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} - case VIDIOC_S_TUNER: - mask = itv->card->gpio_audio_mode.mask; - switch (tuner->audmode) { - case V4L2_TUNER_MODE_LANG1: - data = itv->card->gpio_audio_mode.lang1; - break; - case V4L2_TUNER_MODE_LANG2: - data = itv->card->gpio_audio_mode.lang2; - break; - case V4L2_TUNER_MODE_MONO: - data = itv->card->gpio_audio_mode.mono; - break; - case V4L2_TUNER_MODE_STEREO: - case V4L2_TUNER_MODE_LANG1_LANG2: - default: - data = itv->card->gpio_audio_mode.stereo; - break; - } - break; +static int subdev_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - case AUDC_SET_RADIO: - mask = itv->card->gpio_audio_input.mask; - data = itv->card->gpio_audio_input.radio; + mask = itv->card->gpio_audio_mode.mask; + switch (vt->audmode) { + case V4L2_TUNER_MODE_LANG1: + data = itv->card->gpio_audio_mode.lang1; + break; + case V4L2_TUNER_MODE_LANG2: + data = itv->card->gpio_audio_mode.lang2; + break; + case V4L2_TUNER_MODE_MONO: + data = itv->card->gpio_audio_mode.mono; break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + default: + data = itv->card->gpio_audio_mode.stereo; + break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_s_radio(struct v4l2_subdev *sd) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.radio; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_S_STD: - mask = itv->card->gpio_audio_input.mask; +static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.tuner; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; + + if (route->input > 2) + return -EINVAL; + mask = itv->card->gpio_audio_input.mask; + switch (route->input) { + case 0: data = itv->card->gpio_audio_input.tuner; break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - if (route->input > 2) - return -EINVAL; - mask = itv->card->gpio_audio_input.mask; - switch (route->input) { - case 0: - data = itv->card->gpio_audio_input.tuner; - break; - case 1: - data = itv->card->gpio_audio_input.linein; - break; - case 2: - default: - data = itv->card->gpio_audio_input.radio; - break; - } + case 1: + data = itv->card->gpio_audio_input.linein; + break; + case 2: + default: + data = itv->card->gpio_audio_input.radio; break; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = itv->card->gpio_audio_mute.mute; - ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; - return 0; +static int subdev_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - mask = itv->card->gpio_audio_mute.mask; - data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; - break; + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = itv->card->gpio_audio_mute.mute; + ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; + return 0; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - if (qc->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - *qc = gpio_ctrl_mute; - return 0; - } + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} + +static int subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + if (qc->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + *qc = gpio_ctrl_mute; + return 0; +} + +static int subdev_log_status(struct v4l2_subdev *sd) +{ + struct ivtv *itv = sd_to_ivtv(sd); - case VIDIOC_LOG_STATUS: - IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", + IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), read_reg(IVTV_REG_GPIO_IN)); - return 0; + return 0; +} - case VIDIOC_INT_S_VIDEO_ROUTING: - if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ - return -EINVAL; - mask = itv->card->gpio_video_input.mask; - if (route->input == 0) - data = itv->card->gpio_video_input.tuner; - else if (route->input == 1) - data = itv->card->gpio_video_input.composite; - else - data = itv->card->gpio_video_input.svideo; - break; +static int subdev_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct ivtv *itv = sd_to_ivtv(sd); + u16 mask, data; - default: + if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ return -EINVAL; - } + mask = itv->card->gpio_video_input.mask; + if (route->input == 0) + data = itv->card->gpio_video_input.tuner; + else if (route->input == 1) + data = itv->card->gpio_video_input.composite; + else + data = itv->card->gpio_video_input.svideo; if (mask) write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); return 0; } + +static const struct v4l2_subdev_core_ops subdev_core_ops = { + .log_status = subdev_log_status, + .g_ctrl = subdev_g_ctrl, + .s_ctrl = subdev_s_ctrl, + .queryctrl = subdev_queryctrl, +}; + +static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { + .s_std = subdev_s_std, + .s_radio = subdev_s_radio, + .g_tuner = subdev_g_tuner, + .s_tuner = subdev_s_tuner, +}; + +static const struct v4l2_subdev_audio_ops subdev_audio_ops = { + .s_clock_freq = subdev_s_clock_freq, + .s_routing = subdev_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops subdev_video_ops = { + .s_routing = subdev_s_video_routing, +}; + +static const struct v4l2_subdev_ops subdev_ops = { + .core = &subdev_core_ops, + .tuner = &subdev_tuner_ops, + .audio = &subdev_audio_ops, + .video = &subdev_video_ops, +}; + +int ivtv_gpio_init(struct ivtv *itv) +{ + u16 pin = 0; + + if (itv->card->xceive_pin) + pin = 1 << itv->card->xceive_pin; + + if ((itv->card->gpio_init.direction | pin) == 0) + return 0; + + IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); + + /* init output data then direction */ + write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR); + v4l2_subdev_init(&itv->sd_gpio, &subdev_ops); + snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->device.name); + itv->sd_gpio.grp_id = IVTV_HW_GPIO; + return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio); +} diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.h b/linux/drivers/media/video/ivtv/ivtv-gpio.h index 48b629161..0b5d19c8e 100644 --- a/linux/drivers/media/video/ivtv/ivtv-gpio.h +++ b/linux/drivers/media/video/ivtv/ivtv-gpio.h @@ -22,9 +22,8 @@ #define IVTV_GPIO_H /* GPIO stuff */ -void ivtv_gpio_init(struct ivtv *itv); +int ivtv_gpio_init(struct ivtv *itv); void ivtv_reset_ir_gpio(struct ivtv *itv); int ivtv_reset_tuner_gpio(void *dev, int component, int cmd, int value); -int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg); #endif diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index fa18eae17..41452e111 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -90,26 +90,6 @@ #define IVTV_M52790_I2C_ADDR 0x48 /* This array should match the IVTV_HW_ defines */ -static const u8 hw_driverids[] = { - I2C_DRIVERID_CX25840, - I2C_DRIVERID_SAA711X, - I2C_DRIVERID_SAA7127, - I2C_DRIVERID_MSP3400, - I2C_DRIVERID_TUNER, - I2C_DRIVERID_WM8775, - I2C_DRIVERID_CS53L32A, - I2C_DRIVERID_TVEEPROM, - I2C_DRIVERID_SAA711X, - I2C_DRIVERID_UPD64031A, - I2C_DRIVERID_UPD64083, - I2C_DRIVERID_SAA717X, - I2C_DRIVERID_WM8739, - I2C_DRIVERID_VP27SMPX, - I2C_DRIVERID_M52790, - 0 /* IVTV_HW_GPIO dummy driver ID */ -}; - -/* This array should match the IVTV_HW_ defines */ static const u8 hw_addrs[] = { IVTV_CX25840_I2C_ADDR, IVTV_SAA7115_I2C_ADDR, @@ -130,6 +110,26 @@ static const u8 hw_addrs[] = { }; /* This array should match the IVTV_HW_ defines */ +static const char *hw_modules[] = { + "cx25840", + "saa7115", + "saa7127", + "msp3400", + "tuner", + "wm8775", + "cs53l32a", + NULL, + "saa7115", + "upd64031a", + "upd64083", + "saa717x", + "wm8739", + "vp27smpx", + "m52790", + NULL +}; + +/* This array should match the IVTV_HW_ defines */ static const char * const hw_devicenames[] = { "cx25840", "saa7115", @@ -155,104 +155,58 @@ static const char * const hw_devicenames[] = { int ivtv_i2c_register(struct ivtv *itv, unsigned idx) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) - struct i2c_board_info info; - struct i2c_client *c; - u8 id; - int i; + struct v4l2_subdev *sd; + struct i2c_adapter *adap = &itv->i2c_adap; + const char *mod = hw_modules[idx]; + const char *type = hw_devicenames[idx]; + u32 hw = 1 << idx; - IVTV_DEBUG_I2C("i2c client register\n"); - if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0) + if (idx >= ARRAY_SIZE(hw_addrs)) return -1; - id = hw_driverids[idx]; - memset(&info, 0, sizeof(info)); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) - strlcpy(info.driver_name, hw_devicenames[idx], - sizeof(info.driver_name)); -#else - strlcpy(info.type, hw_devicenames[idx], sizeof(info.type)); -#endif - info.addr = hw_addrs[idx]; - for (i = 0; itv->i2c_clients[i] && i < I2C_CLIENTS_MAX; i++) {} - - if (i == I2C_CLIENTS_MAX) { - IVTV_ERR("insufficient room for new I2C client!\n"); - return -ENOMEM; + if (hw == IVTV_HW_TUNER) { + /* special tuner handling */ + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->radio); + if (sd) + sd->grp_id = 1 << idx; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->demod); + if (sd) + sd->grp_id = 1 << idx; + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + itv->card_i2c->tv); + if (sd) + sd->grp_id = 1 << idx; + return sd ? 0 : -1; } + if (!hw_addrs[idx]) + return -1; + if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { + unsigned short addrs[2] = { hw_addrs[idx], I2C_CLIENT_END }; - if (id != I2C_DRIVERID_TUNER) { - if (id == I2C_DRIVERID_UPD64031A || - id == I2C_DRIVERID_UPD64083) { - unsigned short addrs[2] = { info.addr, I2C_CLIENT_END }; - - c = i2c_new_probed_device(&itv->i2c_adap, &info, addrs); - } else - c = i2c_new_device(&itv->i2c_adap, &info); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i] = c; - return itv->i2c_clients[i] ? 0 : -ENODEV; - } - - /* special tuner handling */ - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->radio); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->demod); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - c = i2c_new_probed_device(&itv->i2c_adap, &info, itv->card_i2c->tv); - if (c && c->driver == NULL) - i2c_unregister_device(c); - else if (c) - itv->i2c_clients[i++] = c; - return 0; -#else - return 0; -#endif -} - -static int attach_inform(struct i2c_client *client) -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) - struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); - int i; - - IVTV_DEBUG_I2C("i2c client attach\n"); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (itv->i2c_clients[i] == NULL) { - itv->i2c_clients[i] = client; - break; - } - } - if (i == I2C_CLIENTS_MAX) { - IVTV_ERR("Insufficient room for new I2C client\n"); + sd = v4l2_i2c_new_probed_subdev(adap, mod, type, addrs); + } else { + sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]); } -#endif - return 0; + if (sd) + sd->grp_id = 1 << idx; + return sd ? 0 : -1; } -static int detach_inform(struct i2c_client *client) +struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw) { - int i; - struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + struct v4l2_subdev *result = NULL; + struct v4l2_subdev *sd; - IVTV_DEBUG_I2C("i2c client detach\n"); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - if (itv->i2c_clients[i] == client) { - itv->i2c_clients[i] = NULL; + spin_lock(&itv->device.lock); + v4l2_device_for_each_subdev(sd, &itv->device) { + if (sd->grp_id == hw) { + result = sd; break; } } - IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n", - client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); - - return 0; + spin_unlock(&itv->device.lock); + return result; } /* Set the serial clock line to the desired state */ @@ -522,7 +476,8 @@ static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, intervening stop condition */ static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { - struct ivtv *itv = i2c_get_adapdata(i2c_adap); + struct v4l2_device *drv = i2c_get_adapdata(i2c_adap); + struct ivtv *itv = to_ivtv(drv); int retval; int i; @@ -561,8 +516,6 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = { .id = I2C_HW_B_CX2341X, .algo = &ivtv_algo, .algo_data = NULL, /* filled from template */ - .client_register = attach_inform, - .client_unregister = detach_inform, .owner = THIS_MODULE, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) .class = I2C_CLASS_TV_ANALOG, @@ -617,8 +570,6 @@ static struct i2c_adapter ivtv_i2c_adap_template = { .id = I2C_HW_B_CX2341X, .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ - .client_register = attach_inform, - .client_unregister = detach_inform, .owner = THIS_MODULE, #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) .class = I2C_CLASS_TV_ANALOG, @@ -638,160 +589,6 @@ static struct i2c_client ivtv_i2c_client_template = { .name = "ivtv internal", }; -int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg) -{ - struct i2c_client *client; - int retval; - int i; - - IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = itv->i2c_clients[i]; - if (client == NULL || client->driver == NULL || - client->driver->command == NULL) - continue; - if (addr == client->addr) { - retval = client->driver->command(client, cmd, arg); - return retval; - } - } - if (cmd != VIDIOC_G_CHIP_IDENT) - IVTV_ERR("i2c addr 0x%02x not found for command 0x%x\n", addr, cmd); - return -ENODEV; -} - -/* Find the i2c device based on the driver ID and return - its i2c address or -ENODEV if no matching device was found. */ -static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id) -{ - struct i2c_client *client; - int retval = -ENODEV; - int i; - - for (i = 0; i < I2C_CLIENTS_MAX; i++) { - client = itv->i2c_clients[i]; - if (client == NULL || client->driver == NULL) - continue; - if (id == client->driver->id) { - retval = client->addr; - break; - } - } - return retval; -} - -/* Find the i2c device name matching the DRIVERID */ -static const char *ivtv_i2c_id_name(u32 id) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (hw_driverids[i] == id) - return hw_devicenames[i]; - return "unknown device"; -} - -/* Find the i2c device name matching the IVTV_HW_ flag */ -static const char *ivtv_i2c_hw_name(u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return hw_devicenames[i]; - return "unknown device"; -} - -/* Find the i2c device matching the IVTV_HW_ flag and return - its i2c address or -ENODEV if no matching device was found. */ -int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) - if (1 << i == hw) - return ivtv_i2c_id_addr(itv, hw_driverids[i]); - return -ENODEV; -} - -/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing. - If hw == IVTV_HW_GPIO then call the gpio handler. */ -int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg) -{ - int addr; - - if (hw == IVTV_HW_GPIO) - return ivtv_gpio(itv, cmd, arg); - if (hw == 0) - return 0; - - addr = ivtv_i2c_hw_addr(itv, hw); - if (addr < 0) { - IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x\n", - hw, ivtv_i2c_hw_name(hw), cmd); - return addr; - } - return ivtv_call_i2c_client(itv, addr, cmd, arg); -} - -/* Calls i2c device based on I2C driver ID. */ -int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg) -{ - int addr; - - addr = ivtv_i2c_id_addr(itv, id); - if (addr < 0) { - if (cmd != VIDIOC_G_CHIP_IDENT) - IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x\n", - id, ivtv_i2c_id_name(id), cmd); - return addr; - } - return ivtv_call_i2c_client(itv, addr, cmd, arg); -} - -int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg); -} - -int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg); -} - -int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg); -} -EXPORT_SYMBOL(ivtv_saa7127); - -int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg); -} - -int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg); -} - -int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg) -{ - return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg); -} - -/* broadcast cmd for all I2C clients and for the gpio subsystem */ -void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg) -{ - if (itv->i2c_adap.algo == NULL) { - IVTV_ERR("Adapter is not set"); - return; - } - i2c_clients_command(&itv->i2c_adap, cmd, arg); - if (itv->hw_flags & IVTV_HW_GPIO) - ivtv_gpio(itv, cmd, arg); -} - /* init + register i2c algo-bit adapter */ int init_ivtv_i2c(struct ivtv *itv) { @@ -800,10 +597,9 @@ int init_ivtv_i2c(struct ivtv *itv) /* Sanity checks for the I2C hardware arrays. They must be the * same size and GPIO must be the last entry. */ - if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || - IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1)) || - hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) { + if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) || + IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) { IVTV_ERR("Mismatched I2C hardware arrays\n"); return -ENODEV; } @@ -820,8 +616,8 @@ int init_ivtv_i2c(struct ivtv *itv) itv->i2c_adap.algo_data = &itv->i2c_algo; sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", - itv->num); - i2c_set_adapdata(&itv->i2c_adap, itv); + itv->instance); + i2c_set_adapdata(&itv->i2c_adap, &itv->device); memcpy(&itv->i2c_client, &ivtv_i2c_client_template, sizeof(struct i2c_client)); diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.h b/linux/drivers/media/video/ivtv/ivtv-i2c.h index 022978cf5..396928a06 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.h +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.h @@ -21,19 +21,8 @@ #ifndef IVTV_I2C_H #define IVTV_I2C_H -int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg); -int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg); - -int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw); -int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg); -int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg); -int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg); -void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg); int ivtv_i2c_register(struct ivtv *itv, unsigned idx); +struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw); /* init + register i2c algo-bit adapter */ int init_ivtv_i2c(struct ivtv *itv); diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index 4bae38d21..cd990a4b8 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -393,7 +393,7 @@ static int ivtv_g_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo return 0; } - itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); vbifmt->service_set = ivtv_get_service_set(vbifmt); return 0; } @@ -581,7 +581,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f p->height = h; if (p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1) fmt->fmt.pix.width /= 2; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); return ivtv_g_fmt_vid_cap(file, fh, fmt); } @@ -593,7 +593,7 @@ static int ivtv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f return -EBUSY; itv->vbi.sliced_in->service_set = 0; itv->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); return ivtv_g_fmt_vbi_cap(file, fh, fmt); } @@ -611,7 +611,7 @@ static int ivtv_s_fmt_sliced_vbi_cap(struct file *file, void *fh, struct v4l2_fo if (ivtv_raw_vbi(itv) && atomic_read(&itv->capturing) > 0) return -EBUSY; itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + v4l2_subdev_call(itv->sd_video, video, s_fmt, fmt); memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); return 0; } @@ -685,18 +685,17 @@ static int ivtv_g_chip_ident(struct file *file, void *fh, struct v4l2_chip_ident chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; return 0; } - if (chip->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); - if (chip->match_type == V4L2_CHIP_MATCH_I2C_ADDR) - return ivtv_call_i2c_client(itv, chip->match_chip, VIDIOC_G_CHIP_IDENT, chip); - return -EINVAL; + if (chip->match_type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match_type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + /* TODO: is this correct? */ + return ivtv_call_all_err(itv, core, g_chip_ident, chip); } #ifdef CONFIG_VIDEO_ADV_DEBUG static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) { struct v4l2_register *regs = arg; - unsigned long flags; volatile u8 __iomem *reg_start; if (!capable(CAP_SYS_ADMIN)) @@ -711,12 +710,10 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) else return -EINVAL; - spin_lock_irqsave(&ivtv_cards_lock, flags); if (cmd == VIDIOC_DBG_G_REGISTER) regs->val = readl(regs->reg + reg_start); else writel(regs->val, regs->reg + reg_start); - spin_unlock_irqrestore(&ivtv_cards_lock, flags); return 0; } @@ -726,9 +723,10 @@ static int ivtv_g_register(struct file *file, void *fh, struct v4l2_register *re if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) return ivtv_itvc(itv, VIDIOC_DBG_G_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); - return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_G_REGISTER, reg); + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + ivtv_call_all(itv, core, g_register, reg); + return 0; } static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *reg) @@ -737,9 +735,10 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_register *re if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) return ivtv_itvc(itv, VIDIOC_DBG_S_REGISTER, reg); - if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) - return ivtv_i2c_id(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); - return ivtv_call_i2c_client(itv, reg->match_chip, VIDIOC_DBG_S_REGISTER, reg); + /* TODO: subdev errors should not be ignored, this should become a + subdev helper function. */ + ivtv_call_all(itv, core, s_register, reg); + return 0; } #endif @@ -884,12 +883,6 @@ static int ivtv_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) streamtype = id->type; - if (ivtv_debug & IVTV_DBGFLG_IOCTL) { - printk(KERN_INFO "ivtv%d ioctl: ", itv->num); - /* Should be replaced */ - /* v4l_printk_ioctl(VIDIOC_S_CROP); */ - } - if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { if (streamtype == IVTV_DEC_STREAM_TYPE_YUV) { @@ -1050,7 +1043,7 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) itv->active_output = outp; route.input = SAA7127_INPUT_TYPE_NORMAL; route.output = itv->card->video_outputs[outp].video_output; - ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, &route); return 0; } @@ -1062,7 +1055,7 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency * if (vf->tuner != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_G_FREQUENCY, vf); + ivtv_call_all(itv, tuner, g_frequency, vf); return 0; } @@ -1075,7 +1068,7 @@ int ivtv_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) ivtv_mute(itv); IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); - ivtv_call_i2c_clients(itv, VIDIOC_S_FREQUENCY, vf); + ivtv_call_all(itv, tuner, s_frequency, vf); ivtv_unmute(itv); return 0; } @@ -1123,14 +1116,14 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std); /* Tuner */ - ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + ivtv_call_all(itv, tuner, s_std, itv->std); if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { /* set display standard */ itv->std_out = *std; itv->is_out_60hz = itv->is_60hz; itv->is_out_50hz = itv->is_50hz; - ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); + ivtv_call_all(itv, video, s_std_output, itv->std_out); ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); itv->main_rect.left = itv->main_rect.top = 0; itv->main_rect.width = 720; @@ -1154,7 +1147,7 @@ static int ivtv_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); + ivtv_call_all(itv, tuner, s_tuner, vt); return 0; } @@ -1166,7 +1159,7 @@ static int ivtv_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) if (vt->index != 0) return -EINVAL; - ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + ivtv_call_all(itv, tuner, g_tuner, vt); if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { strlcpy(vt->name, "ivtv Radio Tuner", sizeof(vt->name)); @@ -1444,14 +1437,15 @@ static int ivtv_log_status(struct file *file, void *fh) struct v4l2_audio audin; int i; - IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); + IVTV_INFO("================= START STATUS CARD #%d =================\n", + itv->instance); IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name); if (itv->hw_flags & IVTV_HW_TVEEPROM) { struct tveeprom tv; ivtv_read_eeprom(itv, &tv); } - ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); + ivtv_call_all(itv, core, log_status); ivtv_get_input(itv, itv->active_input, &vidin); ivtv_get_audio_input(itv, itv->audio_input, &audin); IVTV_INFO("Video Input: %s\n", vidin.name); @@ -1518,7 +1512,7 @@ static int ivtv_log_status(struct file *file, void *fh) } IVTV_INFO("Tuner: %s\n", test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); - cx2341x_log_status(&itv->params, itv->name); + cx2341x_log_status(&itv->params, itv->device.name); IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); for (i = 0; i < IVTV_MAX_STREAMS; i++) { struct ivtv_stream *s = &itv->streams[i]; @@ -1530,8 +1524,11 @@ static int ivtv_log_status(struct file *file, void *fh) (s->buffers * s->buf_size) / 1024, s->buffers); } - IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", (long long)itv->mpg_data_received, (long long)itv->vbi_data_inserted); - IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); + IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n", + (long long)itv->mpg_data_received, + (long long)itv->vbi_data_inserted); + IVTV_INFO("================== END STATUS CARD #%d ==================\n", + itv->instance); return 0; } @@ -1736,7 +1733,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg) case VIDIOC_INT_S_AUDIO_ROUTING: { struct v4l2_routing *route = arg; - ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); + ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, route); break; } @@ -1746,7 +1743,7 @@ static int ivtv_default(struct file *file, void *fh, int cmd, void *arg) if ((val == 0 && itv->options.newi2c) || (val & 0x01)) ivtv_reset_ir_gpio(itv); if (val & 0x02) - itv->video_dec_func(itv, cmd, NULL); + v4l2_subdev_call(itv->sd_video, core, reset, 0); break; } diff --git a/linux/drivers/media/video/ivtv/ivtv-routing.c b/linux/drivers/media/video/ivtv/ivtv-routing.c index 05564919b..3fd302294 100644 --- a/linux/drivers/media/video/ivtv/ivtv-routing.c +++ b/linux/drivers/media/video/ivtv/ivtv-routing.c @@ -47,13 +47,13 @@ void ivtv_audio_set_io(struct ivtv *itv) route.output = 0; if (itv->card->hw_muxer & IVTV_HW_M52790) route.output = M52790_OUT_STEREO; - ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + v4l2_subdev_call(itv->sd_muxer, audio, s_routing, &route); route.input = in->audio_input; route.output = 0; if (itv->card->hw_audio & IVTV_HW_MSP34XX) route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route); + ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, &route); } /* Selects the video input and output according to the current @@ -66,7 +66,7 @@ void ivtv_video_set_io(struct ivtv *itv) route.input = itv->card->video_inputs[inp].video_input; route.output = 0; - itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + v4l2_subdev_call(itv->sd_video, video, s_routing, &route); type = itv->card->video_inputs[inp].video_type; @@ -79,7 +79,7 @@ void ivtv_video_set_io(struct ivtv *itv) } if (itv->card->hw_video & IVTV_HW_GPIO) - ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, &route); if (itv->card->hw_video & IVTV_HW_UPD64031A) { if (type == IVTV_CARD_INPUT_VID_TUNER || @@ -92,7 +92,7 @@ void ivtv_video_set_io(struct ivtv *itv) } route.input |= itv->card->gr_config; - ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, &route); } if (itv->card->hw_video & IVTV_HW_UPD6408X) { @@ -110,6 +110,6 @@ void ivtv_video_set_io(struct ivtv *itv) route.input |= UPD64083_EXT_Y_ADC; } } - ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, &route); } } diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 9b7aa79eb..76279ed30 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -172,7 +172,7 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) { struct ivtv_stream *s = &itv->streams[type]; int num_offset = ivtv_stream_info[type].num_offset; - int num = itv->num + ivtv_first_minor + num_offset; + int num = itv->instance + ivtv_first_minor + num_offset; /* These four fields are always initialized. If v4l2dev == NULL, then this stream is not in use. In that case no other fields but these @@ -205,8 +205,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) return -ENOMEM; } - snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s", - itv->num, s->name); + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s", + itv->device.name, s->name); s->v4l2dev->num = num; s->v4l2dev->parent = &itv->dev->dev; @@ -260,6 +260,7 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) if (s_mpg->v4l2dev) num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset; } + video_set_drvdata(s->v4l2dev, s); /* Register device. First try the desired minor, then any free one. */ if (video_register_device(s->v4l2dev, vfl_type, num)) { @@ -343,7 +344,7 @@ static void ivtv_vbi_setup(struct ivtv *itv) ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); /* setup VBI registers */ - itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + v4l2_subdev_call(itv->sd_video, video, s_fmt, &itv->vbi.in); /* determine number of lines and total number of VBI bytes. A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 @@ -577,10 +578,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) clear_bit(IVTV_F_I_EOS, &itv->i_flags); /* Initialize Digitizer for Capture */ - itv->video_dec_func(itv, VIDIOC_STREAMOFF, NULL); + v4l2_subdev_call(itv->sd_video, video, s_stream, 0); ivtv_msleep_timeout(300, 1); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); - itv->video_dec_func(itv, VIDIOC_STREAMON, NULL); + v4l2_subdev_call(itv->sd_video, video, s_stream, 1); } /* begin_capture */ diff --git a/linux/drivers/media/video/ivtv/ivtv-vbi.c b/linux/drivers/media/video/ivtv/ivtv-vbi.c index 4a37a7d2e..5c5d1c462 100644 --- a/linux/drivers/media/video/ivtv/ivtv-vbi.c +++ b/linux/drivers/media/video/ivtv/ivtv-vbi.c @@ -21,6 +21,7 @@ #include "ivtv-i2c.h" #include "ivtv-ioctl.h" #include "ivtv-queue.h" +#include "ivtv-cards.h" #include "ivtv-vbi.h" static void ivtv_set_vps(struct ivtv *itv, int enabled) @@ -37,7 +38,7 @@ static void ivtv_set_vps(struct ivtv *itv, int enabled) data.data[9] = itv->vbi.vps_payload.data[2]; data.data[10] = itv->vbi.vps_payload.data[3]; data.data[11] = itv->vbi.vps_payload.data[4]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) @@ -51,12 +52,12 @@ static void ivtv_set_cc(struct ivtv *itv, int mode, const struct vbi_cc *cc) data.line = (mode & 1) ? 21 : 0; data.data[0] = cc->odd[0]; data.data[1] = cc->odd[1]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); data.field = 1; data.line = (mode & 2) ? 21 : 0; data.data[0] = cc->even[0]; data.data[1] = cc->even[1]; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) @@ -79,7 +80,7 @@ static void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) data.line = enabled ? 23 : 0; data.data[0] = mode & 0xff; data.data[1] = (mode >> 8) & 0xff; - ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_vbi_data, &data); } static int odd_parity(u8 c) @@ -313,7 +314,7 @@ static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 continue; } vbi.p = p + 4; - itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + v4l2_subdev_call(itv->sd_video, video, decode_vbi_line, &vbi); if (vbi.type && !(lines & (1 << vbi.line))) { lines |= 1 << vbi.line; itv->vbi.sliced_data[line].id = vbi.type; @@ -437,7 +438,7 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_WSS_625; data.field = 0; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { ivtv_set_wss(itv, 1, data.data[0] & 0xf); vi->wss_missing_cnt = 0; } else if (vi->wss_missing_cnt == 4) { @@ -451,13 +452,13 @@ void ivtv_vbi_work_handler(struct ivtv *itv) data.id = V4L2_SLICED_CAPTION_525; data.field = 0; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { mode |= 1; cc.odd[0] = data.data[0]; cc.odd[1] = data.data[1]; } data.field = 1; - if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + if (v4l2_subdev_call(itv->sd_video, video, g_vbi_data, &data) == 0) { mode |= 2; cc.even[0] = data.data[0]; cc.even[1] = data.data[1]; diff --git a/linux/drivers/media/video/ivtv/ivtvfb.c b/linux/drivers/media/video/ivtv/ivtvfb.c index 5e3613d86..26b91d2c8 100644 --- a/linux/drivers/media/video/ivtv/ivtvfb.c +++ b/linux/drivers/media/video/ivtv/ivtvfb.c @@ -48,6 +48,7 @@ #endif #include "ivtv-driver.h" +#include "ivtv-cards.h" #include "ivtv-i2c.h" #include "ivtv-udma.h" #include "ivtv-mailbox.h" @@ -121,15 +122,15 @@ MODULE_LICENSE("GPL"); #define IVTVFB_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtvfb_debug) \ - printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->num , ## args); \ + printk(KERN_INFO "ivtvfb%d " type ": " fmt, itv->instance , ## args); \ } while (0) #define IVTVFB_DEBUG_WARN(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_WARN, "warning", fmt , ## args) #define IVTVFB_DEBUG_INFO(fmt, args...) IVTVFB_DEBUG(IVTVFB_DBGFLG_INFO, "info", fmt , ## args) /* Standard kernel messages */ -#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->num , ## args) -#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->num , ## args) -#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->num , ## args) +#define IVTVFB_ERR(fmt, args...) printk(KERN_ERR "ivtvfb%d: " fmt, itv->instance , ## args) +#define IVTVFB_WARN(fmt, args...) printk(KERN_WARNING "ivtvfb%d: " fmt, itv->instance , ## args) +#define IVTVFB_INFO(fmt, args...) printk(KERN_INFO "ivtvfb%d: " fmt, itv->instance , ## args) /* --------------------------------------------------------------------- */ @@ -904,16 +905,16 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info) switch (blank_mode) { case FB_BLANK_UNBLANK: ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 1); - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); break; case FB_BLANK_NORMAL: case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_VSYNC_SUSPEND: ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); - ivtv_saa7127(itv, VIDIOC_STREAMON, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 1); break; case FB_BLANK_POWERDOWN: - ivtv_saa7127(itv, VIDIOC_STREAMOFF, NULL); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_stream, 0); ivtv_vapi(itv, CX2341X_OSD_SET_STATE, 1, 0); break; } @@ -1197,10 +1198,45 @@ static int ivtvfb_init_card(struct ivtv *itv) } +static int __init ivtvfb_callback_init(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (ivtvfb_init_card(itv) == 0) { + IVTVFB_INFO("Framebuffer registered on %s\n", + itv->device.name); + (*(int *)p)++; + } + } + return 0; +} + +static int ivtvfb_callback_cleanup(struct device *dev, void *p) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device); + + if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { + if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { + IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", + itv->instance); + return 0; + } + IVTVFB_INFO("Unregister framebuffer %d\n", itv->instance); + ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); + ivtvfb_release_buffers(itv); + itv->osd_video_pbase = 0; + } + return 0; +} + static int __init ivtvfb_init(void) { - struct ivtv *itv; - int i, registered = 0; + struct device_driver *drv; + int registered = 0; + int err; if (ivtvfb_card_id < -1 || ivtvfb_card_id >= IVTV_MAX_CARDS) { printk(KERN_ERR "ivtvfb: ivtvfb_card_id parameter is out of range (valid range: -1 - %d)\n", @@ -1208,20 +1244,11 @@ static int __init ivtvfb_init(void) return -EINVAL; } - /* Locate & initialise all cards supporting an OSD. */ - for (i = 0; i < ivtv_cards_active; i++) { - if (ivtvfb_card_id != -1 && i != ivtvfb_card_id) - continue; - itv = ivtv_cards[i]; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) { - if (ivtvfb_init_card(itv) == 0) { - IVTVFB_INFO("Framebuffer registered on ivtv card id %d\n", i); - registered++; - } - } - } + drv = driver_find("ivtv", &pci_bus_type); + err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); + put_driver(drv); if (!registered) { - printk(KERN_ERR "ivtvfb: no cards found"); + printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; } return 0; @@ -1229,24 +1256,14 @@ static int __init ivtvfb_init(void) static void ivtvfb_cleanup(void) { - struct ivtv *itv; - int i; + struct device_driver *drv; + int err; printk(KERN_INFO "ivtvfb: Unloading framebuffer module\n"); - for (i = 0; i < ivtv_cards_active; i++) { - itv = ivtv_cards[i]; - if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) && itv->osd_info) { - if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) { - IVTVFB_WARN("Framebuffer %d is in use, cannot unload\n", i); - return; - } - IVTVFB_DEBUG_INFO("Unregister framebuffer %d\n", i); - ivtvfb_blank(FB_BLANK_POWERDOWN, &itv->osd_info->ivtvfb_info); - ivtvfb_release_buffers(itv); - itv->osd_video_pbase = 0; - } - } + drv = driver_find("ivtv", &pci_bus_type); + err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); + put_driver(drv); } module_init(ivtvfb_init); diff --git a/linux/drivers/media/video/m52790.c b/linux/drivers/media/video/m52790.c index f39da4a36..1976451e0 100644 --- a/linux/drivers/media/video/m52790.c +++ b/linux/drivers/media/video/m52790.c @@ -28,7 +28,7 @@ #include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/m52790.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include "compat.h" @@ -44,89 +44,130 @@ I2C_CLIENT_INSMOD; #endif struct m52790_state { + struct v4l2_subdev sd; u16 input; u16 output; }; +static inline struct m52790_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct m52790_state, sd); +} + /* ----------------------------------------------------------------------- */ -static int m52790_write(struct i2c_client *client) +static int m52790_write(struct v4l2_subdev *sd) { - struct m52790_state *state = i2c_get_clientdata(client); + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 sw1 = (state->input | state->output) & 0xff; u8 sw2 = (state->input | state->output) >> 8; return i2c_smbus_write_byte_data(client, sw1, sw2); } -static int m52790_command(struct i2c_client *client, unsigned int cmd, - void *arg) +/* Note: audio and video are linked and cannot be switched separately. + So audio and video routing commands are identical for this chip. + In theory the video amplifier and audio modes could be handled + separately for the output, but that seems to be overkill right now. + The same holds for implementing an audio mute control, this is now + part of the audio output routing. The normal case is that another + chip takes care of the actual muting so making it part of the + output routing seems to be the right thing to do for now. */ +static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct m52790_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - /* Note: audio and video are linked and cannot be switched separately. - So audio and video routing commands are identical for this chip. - In theory the video amplifier and audio modes could be handled - separately for the output, but that seems to be overkill right now. - The same holds for implementing an audio mute control, this is now - part of the audio output routing. The normal case is that another - chip takes care of the actual muting so making it part of the - output routing seems to be the right thing to do for now. */ - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->input; - route->output = state->output; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - case VIDIOC_INT_S_VIDEO_ROUTING: - state->input = route->input; - state->output = route->output; - m52790_write(client); - break; + struct m52790_state *state = to_state(sd); + + state->input = route->input; + state->output = route->output; + m52790_write(sd); + return 0; +} #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (reg->reg != 0) - return -EINVAL; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = state->input | state->output; - else { - state->input = reg->val & 0x0303; - state->output = reg->val & ~0x0303; - m52790_write(client); - } - break; - } -#endif +static int m52790_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_M52790, 0); + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + reg->val = state->input | state->output; + return 0; +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Switch 1: %02x\n", - (state->input | state->output) & 0xff); - v4l_info(client, "Switch 2: %02x\n", - (state->input | state->output) >> 8); - break; +static int m52790_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct m52790_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - default: + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) return -EINVAL; - } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (reg->reg != 0) + return -EINVAL; + state->input = reg->val & 0x0303; + state->output = reg->val & ~0x0303; + m52790_write(sd); return 0; } +#endif + +static int m52790_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_M52790, 0); +} + +static int m52790_log_status(struct v4l2_subdev *sd) +{ + struct m52790_state *state = to_state(sd); + + v4l2_info(sd, "Switch 1: %02x\n", + (state->input | state->output) & 0xff); + v4l2_info(sd, "Switch 2: %02x\n", + (state->input | state->output) >> 8); + return 0; +} + +static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops m52790_core_ops = { + .log_status = m52790_log_status, + .g_chip_ident = m52790_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = m52790_g_register, + .s_register = m52790_s_register, +#endif +}; + +static const struct v4l2_subdev_audio_ops m52790_audio_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_video_ops m52790_video_ops = { + .s_routing = m52790_s_routing, +}; + +static const struct v4l2_subdev_ops m52790_ops = { + .core = &m52790_core_ops, + .audio = &m52790_audio_ops, + .video = &m52790_video_ops, +}; /* ----------------------------------------------------------------------- */ @@ -136,6 +177,7 @@ static int m52790_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct m52790_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -152,16 +194,20 @@ static int m52790_probe(struct i2c_client *client, if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &m52790_ops); state->input = M52790_IN_TUNER; state->output = M52790_OUT_STEREO; - i2c_set_clientdata(client, state); - m52790_write(client); + m52790_write(sd); return 0; } static int m52790_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index c6c0a584c..def15e381 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -51,18 +51,18 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/i2c.h> -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-i2c-drv-legacy.h> -#include <media/tvaudio.h> -#include <media/msp3400.h> #include <linux/kthread.h> #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) #include <linux/suspend.h> #else #include <linux/freezer.h> #endif +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-i2c-drv-legacy.h> +#include <media/msp3400.h> +#include <media/tvaudio.h> #include "compat.h" #include "msp3400-driver.h" @@ -270,7 +270,7 @@ static char *scart_names[] = { void msp_set_scart(struct i2c_client *client, int in, int out) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); state->in_scart = in; @@ -294,7 +294,7 @@ void msp_set_scart(struct i2c_client *client, int in, int out) void msp_set_audio(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int bal = 0, bass, treble, loudness; int val = 0; int reallymuted = state->muted | state->scan_in_progress; @@ -341,7 +341,7 @@ void msp_set_audio(struct i2c_client *client) static void msp_wake_thread(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (NULL == state->kthread) return; @@ -395,9 +395,9 @@ static int msp_mode_v4l1_to_v4l2(int mode) } #endif -static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -438,9 +438,10 @@ static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: @@ -486,40 +487,16 @@ static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) +#ifdef CONFIG_VIDEO_ALLOW_V4L1 +static int msp_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) { - struct msp_state *state = i2c_get_clientdata(client); - - if (msp_debug >= 2) - v4l_i2c_print_ioctl(client, cmd); + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); switch (cmd) { - case AUDC_SET_RADIO: - if (state->radio) - return 0; - state->radio = 1; - v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); - state->watch_stereo = 0; - switch (state->opmode) { - case OPMODE_MANUAL: - /* set msp3400 to FM radio mode */ - msp3400c_set_mode(client, MSP_MODE_FM_RADIO); - msp3400c_set_carrier(client, MSP_CARRIER(10.7), - MSP_CARRIER(10.7)); - msp_set_audio(client); - break; - case OPMODE_AUTODETECT: - case OPMODE_AUTOSELECT: - /* the thread will do for us */ - msp_wake_thread(client); - break; - } - break; - /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ -#ifdef CONFIG_VIDEO_ALLOW_V4L1 case VIDIOCGAUDIO: { struct video_audio *va = arg; @@ -593,105 +570,137 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp_wake_thread(client); break; } -#endif - case VIDIOC_S_FREQUENCY: - { - /* new channel -- kick audio carrier scan */ - msp_wake_thread(client); - break; + default: + return -ENOIOCTLCMD; } + return 0; +} +#endif - /* --- v4l2 ioctls --- */ - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - int update = state->radio || state->v4l2_std != *id; +/* --- v4l2 ioctls --- */ +static int msp_s_radio(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - state->v4l2_std = *id; - state->radio = 0; - if (update) - msp_wake_thread(client); + if (state->radio) return 0; + state->radio = 1; + v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); + state->watch_stereo = 0; + switch (state->opmode) { + case OPMODE_MANUAL: + /* set msp3400 to FM radio mode */ + msp3400c_set_mode(client, MSP_MODE_FM_RADIO); + msp3400c_set_carrier(client, MSP_CARRIER(10.7), + MSP_CARRIER(10.7)); + msp_set_audio(client); + break; + case OPMODE_AUTODETECT: + case OPMODE_AUTOSELECT: + /* the thread will do for us */ + msp_wake_thread(client); + break; } + return 0; +} - case VIDIOC_INT_G_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; +static int msp_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - *rt = state->routing; - break; - } + /* new channel -- kick audio carrier scan */ + msp_wake_thread(client); + return 0; +} - case VIDIOC_INT_S_AUDIO_ROUTING: - { - struct v4l2_routing *rt = arg; - int tuner = (rt->input >> 3) & 1; - int sc_in = rt->input & 0x7; - int sc1_out = rt->output & 0xf; - int sc2_out = (rt->output >> 4) & 0xf; - u16 val, reg; - int i; - int extern_input = 1; - - if (state->routing.input == rt->input && - state->routing.output == rt->output) - break; - state->routing = *rt; - /* check if the tuner input is used */ - for (i = 0; i < 5; i++) { - if (((rt->input >> (4 + i * 4)) & 0xf) == 0) - extern_input = 0; - } - state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; - state->rxsubchans = V4L2_TUNER_SUB_STEREO; - msp_set_scart(client, sc_in, 0); - msp_set_scart(client, sc1_out, 1); - msp_set_scart(client, sc2_out, 2); - msp_set_audmode(client); - reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; - val = msp_read_dem(client, reg); - msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); - /* wake thread when a new input is chosen */ +static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int update = state->radio || state->v4l2_std != id; + + state->v4l2_std = id; + state->radio = 0; + if (update) msp_wake_thread(client); - break; + return 0; +} + +static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int tuner = (rt->input >> 3) & 1; + int sc_in = rt->input & 0x7; + int sc1_out = rt->output & 0xf; + int sc2_out = (rt->output >> 4) & 0xf; + u16 val, reg; + int i; + int extern_input = 1; + + if (state->routing.input == rt->input && + state->routing.output == rt->output) + return 0; + state->routing = *rt; + /* check if the tuner input is used */ + for (i = 0; i < 5; i++) { + if (((rt->input >> (4 + i * 4)) & 0xf) == 0) + extern_input = 0; } + state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; + state->rxsubchans = V4L2_TUNER_SUB_STEREO; + msp_set_scart(client, sc_in, 0); + msp_set_scart(client, sc1_out, 1); + msp_set_scart(client, sc2_out, 2); + msp_set_audmode(client); + reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; + val = msp_read_dem(client, reg); + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new input is chosen */ + msp_wake_thread(client); + return 0; +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; +static int msp_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio) - break; - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - vt->audmode = state->audmode; - vt->rxsubchans = state->rxsubchans; - vt->capability |= V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - break; - } + if (state->radio) + return 0; + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + vt->audmode = state->audmode; + vt->rxsubchans = state->rxsubchans; + vt->capability |= V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + return 0; +} - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; +static int msp_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio) /* TODO: add mono/stereo support for radio */ - break; - if (state->audmode == vt->audmode) - break; - state->audmode = vt->audmode; - /* only set audmode */ - msp_set_audmode(client); - break; - } + if (state->radio) /* TODO: add mono/stereo support for radio */ + return 0; + if (state->audmode == vt->audmode) + return 0; + state->audmode = vt->audmode; + /* only set audmode */ + msp_set_audmode(client); + return 0; +} - case VIDIOC_INT_I2S_CLOCK_FREQ: - { - u32 *a = (u32 *)arg; +static int msp_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a); + v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", freq); - switch (*a) { + switch (freq) { case 1024000: state->i2s_mode = 0; break; @@ -700,24 +709,24 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; default: return -EINVAL; - } - break; } + return 0; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; +static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + struct msp_state *state = to_state(sd); - switch (qc->id) { + switch (qc->id) { case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_MUTE: return v4l2_ctrl_query_fill_std(qc); default: break; - } - if (!state->has_sound_processing) - return -EINVAL; - switch (qc->id) { + } + if (!state->has_sound_processing) + return -EINVAL; + switch (qc->id) { case V4L2_CID_AUDIO_LOUDNESS: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: @@ -725,32 +734,38 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return v4l2_ctrl_query_fill_std(qc); default: return -EINVAL; - } } + return 0; +} - case VIDIOC_G_CTRL: - return msp_get_ctrl(client, arg); - - case VIDIOC_S_CTRL: - return msp_set_ctrl(client, arg); +static int msp_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - { - const char *p; + return v4l2_chip_ident_i2c_client(client, chip, state->ident, + (state->rev1 << 16) | state->rev2); +} - if (state->opmode == OPMODE_AUTOSELECT) - msp_detect_stereo(client); - v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", - client->name, state->rev1, state->rev2); - v4l_info(client, "Audio: volume %d%s\n", - state->volume, state->muted ? " (muted)" : ""); - if (state->has_sound_processing) { - v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", - state->balance, state->bass, - state->treble, - state->loudness ? "on" : "off"); - } - switch (state->mode) { +static int msp_log_status(struct v4l2_subdev *sd) +{ + struct msp_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + const char *p; + + if (state->opmode == OPMODE_AUTOSELECT) + msp_detect_stereo(client); + v4l_info(client, "%s rev1 = 0x%04x rev2 = 0x%04x\n", + client->name, state->rev1, state->rev2); + v4l_info(client, "Audio: volume %d%s\n", + state->volume, state->muted ? " (muted)" : ""); + if (state->has_sound_processing) { + v4l_info(client, "Audio: balance %d bass %d treble %d loudness %s\n", + state->balance, state->bass, + state->treble, + state->loudness ? "on" : "off"); + } + switch (state->mode) { case MSP_MODE_AM_DETECT: p = "AM (for carrier detect)"; break; case MSP_MODE_FM_RADIO: p = "FM Radio"; break; case MSP_MODE_FM_TERRA: p = "Terrestial FM-mono/stereo"; break; @@ -761,36 +776,25 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) case MSP_MODE_BTSC: p = "BTSC"; break; case MSP_MODE_EXTERN: p = "External input"; break; default: p = "unknown"; break; - } - if (state->mode == MSP_MODE_EXTERN) { - v4l_info(client, "Mode: %s\n", p); - } else if (state->opmode == OPMODE_MANUAL) { - v4l_info(client, "Mode: %s (%s%s)\n", p, + } + if (state->mode == MSP_MODE_EXTERN) { + v4l_info(client, "Mode: %s\n", p); + } else if (state->opmode == OPMODE_MANUAL) { + v4l_info(client, "Mode: %s (%s%s)\n", p, (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } else { - if (state->opmode == OPMODE_AUTODETECT) - v4l_info(client, "Mode: %s\n", p); - v4l_info(client, "Standard: %s (%s%s)\n", + } else { + if (state->opmode == OPMODE_AUTODETECT) + v4l_info(client, "Mode: %s\n", p); + v4l_info(client, "Standard: %s (%s%s)\n", msp_standard_std_name(state->std), (state->rxsubchans & V4L2_TUNER_SUB_STEREO) ? "stereo" : "mono", (state->rxsubchans & V4L2_TUNER_SUB_LANG2) ? ", dual" : ""); - } - v4l_info(client, "Audmode: 0x%04x\n", state->audmode); - v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", - state->routing.input, state->routing.output); - v4l_info(client, "ACB: 0x%04x\n", state->acb); - break; - } - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, - (state->rev1 << 16) | state->rev2); - - default: - /* unknown */ - return -EINVAL; } + v4l_info(client, "Audmode: 0x%04x\n", state->audmode); + v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", + state->routing.input, state->routing.output); + v4l_info(client, "ACB: 0x%04x\n", state->acb); return 0; } @@ -808,11 +812,49 @@ static int msp_resume(struct i2c_client *client) return 0; } +static int msp_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops msp_core_ops = { + .log_status = msp_log_status, + .g_chip_ident = msp_g_chip_ident, + .g_ctrl = msp_g_ctrl, + .s_ctrl = msp_s_ctrl, + .queryctrl = msp_queryctrl, +#ifdef CONFIG_VIDEO_ALLOW_V4L1 + .ioctl = msp_ioctl, +#endif +}; + +static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { + .s_frequency = msp_s_frequency, + .g_tuner = msp_g_tuner, + .s_tuner = msp_s_tuner, + .s_radio = msp_s_radio, + .s_std = msp_s_std, +}; + +static const struct v4l2_subdev_audio_ops msp_audio_ops = { + .s_routing = msp_s_routing, + .s_i2s_clock_freq = msp_s_i2s_clock_freq, +}; + +static const struct v4l2_subdev_ops msp_ops = { + .core = &msp_core_ops, + .tuner = &msp_tuner_ops, + .audio = &msp_audio_ops, +}; + /* ----------------------------------------------------------------------- */ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct msp_state *state; + struct v4l2_subdev *sd; int (*thread_func)(void *data) = NULL; int msp_hard; int msp_family; @@ -836,7 +878,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) if (!state) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &msp_ops); state->v4l2_std = V4L2_STD_NTSC; state->audmode = V4L2_TUNER_MODE_STEREO; @@ -990,8 +1033,9 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) static int msp_remove(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); + v4l2_device_unregister_subdev(&state->sd); /* shutdown control thread */ if (state->kthread) { state->restart = 1; diff --git a/linux/drivers/media/video/msp3400-driver.h b/linux/drivers/media/video/msp3400-driver.h index ab69a290e..3fe1c1b10 100644 --- a/linux/drivers/media/video/msp3400-driver.h +++ b/linux/drivers/media/video/msp3400-driver.h @@ -5,6 +5,7 @@ #define MSP3400_DRIVER_H #include <media/msp3400.h> +#include <media/v4l2-device.h> /* ---------------------------------------------------------------------- */ @@ -49,6 +50,7 @@ extern int msp_dolby; extern int msp_stereo_thresh; struct msp_state { + struct v4l2_subdev sd; int rev1, rev2; int ident; u8 has_nicam; @@ -96,6 +98,11 @@ struct msp_state { unsigned int watch_stereo:1; }; +static inline struct msp_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct msp_state, sd); +} + /* msp3400-driver.c */ int msp_write_dem(struct i2c_client *client, int addr, int val); int msp_write_dsp(struct i2c_client *client, int addr, int val); diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index 29c0d5cf6..ccba71d9f 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -162,7 +162,7 @@ const char *msp_standard_std_name(int std) static void msp_set_source(struct i2c_client *client, u16 src) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (msp_dolby) { msp_write_dsp(client, 0x0008, 0x0520); /* I2S1 */ @@ -189,7 +189,7 @@ void msp3400c_set_carrier(struct i2c_client *client, int cdo1, int cdo2) void msp3400c_set_mode(struct i2c_client *client, int mode) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; int tuner = (state->routing.input >> 3) & 1; int i; @@ -230,7 +230,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) static char *strmode[] = { "mono", "stereo", "lang2", "lang1", "lang1+lang2" }; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); char *modestr = (state->audmode >= 0 && state->audmode < 5) ? strmode[state->audmode] : "unknown"; int src = 0; /* channel source: FM/AM, nicam or SCART */ @@ -359,7 +359,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) static void msp3400c_print_mode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->main == state->second) v4l_dbg(1, msp_debug, client, @@ -388,7 +388,7 @@ static void msp3400c_print_mode(struct i2c_client *client) static int msp3400c_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val; int rxsubchans = state->rxsubchans; int newnicam = state->nicam_on; @@ -466,7 +466,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client) /* stereo/multilang monitoring */ static void watch_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (msp_detect_stereo(client)) msp_set_audmode(client); @@ -478,7 +478,7 @@ static void watch_stereo(struct i2c_client *client) int msp3400c_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); struct msp3400c_carrier_detect *cd; int count, max1, max2, val1, val2, val, i; @@ -662,7 +662,7 @@ no_second: int msp3410d_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val, i, std, count; v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); @@ -828,7 +828,7 @@ restart: static int msp34xxg_modus(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->radio) { v4l_dbg(1, msp_debug, client, "selected radio modus\n"); @@ -855,7 +855,7 @@ static int msp34xxg_modus(struct i2c_client *client) static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int source, matrix; switch (state->audmode) { @@ -898,7 +898,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) static void msp34xxg_set_sources(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); u32 in = state->routing.input; msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); @@ -914,7 +914,7 @@ static void msp34xxg_set_sources(struct i2c_client *client) /* (re-)initialize the msp34xxg */ static void msp34xxg_reset(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int tuner = (state->routing.input >> 3) & 1; int modus; @@ -957,7 +957,7 @@ static void msp34xxg_reset(struct i2c_client *client) int msp34xxg_thread(void *data) { struct i2c_client *client = data; - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int val, i; v4l_dbg(1, msp_debug, client, "msp34xxg daemon started\n"); @@ -1052,7 +1052,7 @@ unmute: static int msp34xxg_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); int status = msp_read_dem(client, 0x0200); int is_bilingual = status & 0x100; int is_stereo = status & 0x40; @@ -1081,7 +1081,7 @@ static int msp34xxg_detect_stereo(struct i2c_client *client) static void msp34xxg_set_audmode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); if (state->std == 0x20) { if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && @@ -1098,7 +1098,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client) void msp_set_audmode(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); switch (state->opmode) { case OPMODE_MANUAL: @@ -1113,7 +1113,7 @@ void msp_set_audmode(struct i2c_client *client) int msp_detect_stereo(struct i2c_client *client) { - struct msp_state *state = i2c_get_clientdata(client); + struct msp_state *state = to_state(i2c_get_clientdata(client)); switch (state->opmode) { case OPMODE_MANUAL: diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 02386e403..e9edd4d37 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -44,7 +44,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> #include <media/saa7115.h> @@ -71,6 +71,7 @@ static unsigned short normal_i2c[] = { I2C_CLIENT_INSMOD; struct saa711x_state { + struct v4l2_subdev sd; v4l2_std_id std; int input; int output; @@ -90,10 +91,17 @@ struct saa711x_state { u8 apll; }; +static inline struct saa711x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa711x_state, sd); +} + /* ----------------------------------------------------------------------- */ -static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value) +static inline int saa711x_write(struct v4l2_subdev *sd, u8 reg, u8 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_write_byte_data(client, reg, value); } @@ -129,9 +137,9 @@ static int saa711x_has_reg(const int id, const u8 reg) return 1; } -static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs) +static int saa711x_writeregs(struct v4l2_subdev *sd, const unsigned char *regs) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); unsigned char reg, data; while (*regs != 0x00) { @@ -140,18 +148,20 @@ static int saa711x_writeregs(struct i2c_client *client, const unsigned char *reg /* According with datasheets, reserved regs should be filled with 0 - seems better not to touch on they */ - if (saa711x_has_reg(state->ident,reg)) { - if (saa711x_write(client, reg, data) < 0) + if (saa711x_has_reg(state->ident, reg)) { + if (saa711x_write(sd, reg, data) < 0) return -1; } else { - v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg); + v4l2_dbg(1, debug, sd, "tried to access reserved reg 0x%02x\n", reg); } } return 0; } -static inline int saa711x_read(struct i2c_client *client, u8 reg) +static inline int saa711x_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } @@ -602,7 +612,7 @@ static int saa711x_odd_parity(u8 c) return c & 1; } -static int saa711x_decode_vps(u8 * dst, u8 * p) +static int saa711x_decode_vps(u8 *dst, u8 *p) { static const u8 biphase_tbl[] = { 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, @@ -649,7 +659,7 @@ static int saa711x_decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -static int saa711x_decode_wss(u8 * p) +static int saa711x_decode_wss(u8 *p) { static const int wss_bits[8] = { 0, 0, 0, 1, 0, 1, 1, 1 @@ -676,9 +686,9 @@ static int saa711x_decode_wss(u8 * p) return wss; } -static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) +static int saa711x_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); u32 acpf; u32 acni; u32 hz; @@ -686,10 +696,10 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) u8 acc = 0; /* reg 0x3a, audio clock control */ /* Checks for chips that don't have audio clock (saa7111, saa7113) */ - if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) + if (!saa711x_has_reg(state->ident, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD)) return 0; - v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); + v4l2_dbg(1, debug, sd, "set audio clock freq: %d\n", freq); /* sanity check */ if (freq < 32000 || freq > 48000) @@ -716,66 +726,66 @@ static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq) if (state->apll) acc |= 0x08; - saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); - saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); - saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); + saa711x_write(sd, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03); + saa711x_write(sd, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10); + saa711x_write(sd, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff); + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1, (acpf >> 8) & 0xff); - saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, + saa711x_write(sd, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2, (acpf >> 16) & 0x03); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); - saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff); + saa711x_write(sd, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f); state->audclk_freq = freq; return 0; } -static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; - saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright); + saa711x_write(sd, R_0A_LUMA_BRIGHT_CNTL, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; - saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast); + saa711x_write(sd, R_0B_LUMA_CONTRAST_CNTL, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; - saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat); + saa711x_write(sd, R_0C_CHROMA_SAT_CNTL, state->sat); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; - saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue); + saa711x_write(sd, R_0D_CHROMA_HUE_CNTL, state->hue); break; default: @@ -785,9 +795,9 @@ static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa711x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -809,16 +819,16 @@ static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa711x_set_size(struct i2c_client *client, int width, int height) +static int saa711x_set_size(struct v4l2_subdev *sd, int width, int height) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); int HPSC, HFSC; int VSCY; int res; int is_50hz = state->std & V4L2_STD_625_50; int Vsrc = is_50hz ? 576 : 480; - v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height); + v4l2_dbg(1, debug, sd, "decoder set size to %ix%i\n", width, height); /* FIXME need better bounds checking here */ if ((width < 1) || (width > 1440)) @@ -826,7 +836,7 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) if ((height < 1) || (height > Vsrc)) return -EINVAL; - if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) { + if (!saa711x_has_reg(state->ident, R_D0_B_HORIZ_PRESCALING)) { /* Decoder only supports 720 columns and 480 or 576 lines */ if (width != 720) return -EINVAL; @@ -844,22 +854,22 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) /* Set output width/height */ /* width */ - saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, (u8) (width & 0xff)); - saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((width >> 8) & 0xff)); /* Vertical Scaling uses height/2 */ - res=height/2; + res = height / 2; /* On 60Hz, it is using a higher Vertical Output Size */ if (!is_50hz) res += (VRES_60HZ - 480) >> 1; /* height */ - saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, + saa711x_write(sd, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH, (u8) (res & 0xff)); - saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, + saa711x_write(sd, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB, (u8) ((res >> 8) & 0xff)); /* Scaling settings */ @@ -870,54 +880,54 @@ static int saa711x_set_size(struct i2c_client *client, int width, int height) HFSC = (int)((1024 * 720) / (HPSC * width)); /* FIXME hardcodes to "Task B" * write H prescaler integer */ - saa711x_write(client, R_D0_B_HORIZ_PRESCALING, + saa711x_write(sd, R_D0_B_HORIZ_PRESCALING, (u8) (HPSC & 0x3f)); - v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); + v4l2_dbg(1, debug, sd, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); /* write H fine-scaling (luminance) */ - saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC, + saa711x_write(sd, R_D8_B_HORIZ_LUMA_SCALING_INC, (u8) (HFSC & 0xff)); - saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, + saa711x_write(sd, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, (u8) ((HFSC >> 8) & 0xff)); /* write H fine-scaling (chrominance) * must be lum/2, so i'll just bitshift :) */ - saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING, + saa711x_write(sd, R_DC_B_HORIZ_CHROMA_SCALING, (u8) ((HFSC >> 1) & 0xff)); - saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB, + saa711x_write(sd, R_DD_B_HORIZ_CHROMA_SCALING_MSB, (u8) ((HFSC >> 9) & 0xff)); VSCY = (int)((1024 * Vsrc) / height); - v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + v4l2_dbg(1, debug, sd, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); /* Correct Contrast and Luminance */ - saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL, + saa711x_write(sd, R_D5_B_LUMA_CONTRAST_CNTL, (u8) (64 * 1024 / VSCY)); - saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL, + saa711x_write(sd, R_D6_B_CHROMA_SATURATION_CNTL, (u8) (64 * 1024 / VSCY)); /* write V fine-scaling (luminance) */ - saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC, + saa711x_write(sd, R_E0_B_VERT_LUMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB, + saa711x_write(sd, R_E1_B_VERT_LUMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); /* write V fine-scaling (chrominance) */ - saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC, + saa711x_write(sd, R_E2_B_VERT_CHROMA_SCALING_INC, (u8) (VSCY & 0xff)); - saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, + saa711x_write(sd, R_E3_B_VERT_CHROMA_SCALING_INC_MSB, (u8) ((VSCY >> 8) & 0xff)); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); /* Activates task "B" */ - saa711x_write(client, R_80_GLOBAL_CNTL_1, - saa711x_read(client,R_80_GLOBAL_CNTL_1) | 0x20); + saa711x_write(sd, R_80_GLOBAL_CNTL_1, + saa711x_read(sd, R_80_GLOBAL_CNTL_1) | 0x20); return 0; } -static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) +static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices @@ -933,13 +943,13 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { - v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_60hz_video); - saa711x_set_size(client, 720, 480); + v4l2_dbg(1, debug, sd, "decoder set standard 60 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_60hz_video); + saa711x_set_size(sd, 720, 480); } else { - v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); - saa711x_writeregs(client, saa7115_cfg_50hz_video); - saa711x_set_size(client, 720, 576); + v4l2_dbg(1, debug, sd, "decoder set standard 50 Hz\n"); + saa711x_writeregs(sd, saa7115_cfg_50hz_video); + saa711x_set_size(sd, 720, 576); } /* Register 0E - Bits D6-D4 on NO-AUTO mode @@ -953,7 +963,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) */ if (state->ident == V4L2_IDENT_SAA7111 || state->ident == V4L2_IDENT_SAA7113) { - u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f; + u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; if (std == V4L2_STD_PAL_M) { reg |= 0x30; @@ -966,87 +976,31 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) } else if (std & V4L2_STD_SECAM) { reg |= 0x50; } - saa711x_write(client, R_0E_CHROMA_CNTL_1, reg); + saa711x_write(sd, R_0E_CHROMA_CNTL_1, reg); } else { /* restart task B if needed */ - int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10; + int taskb = saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10; if (taskb && state->ident == V4L2_IDENT_SAA7114) { - saa711x_writeregs(client, saa7115_cfg_vbi_on); + saa711x_writeregs(sd, saa7115_cfg_vbi_on); } /* switch audio mode too! */ - saa711x_set_audio_clock_freq(client, state->audclk_freq); - } -} - -static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - - return state->std; -} - -static void saa711x_log_status(struct i2c_client *client) -{ - struct saa711x_state *state = i2c_get_clientdata(client); - int reg1e, reg1f; - int signalOk; - int vcr; - - v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq); - if (state->ident != V4L2_IDENT_SAA7115) { - /* status for the saa7114 */ - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - signalOk = (reg1f & 0xc1) == 0x81; - v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - return; - } - - /* status for the saa7115 */ - reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC); - reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); - - signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; - vcr = !(reg1f & 0x10); - - if (state->input >= 6) { - v4l_info(client, "Input: S-Video %d\n", state->input - 6); - } else { - v4l_info(client, "Input: Composite %d\n", state->input); + saa711x_s_clock_freq(sd, state->audclk_freq); } - v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); - v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); - - switch (reg1e & 0x03) { - case 1: - v4l_info(client, "Detected format: NTSC\n"); - break; - case 2: - v4l_info(client, "Detected format: PAL\n"); - break; - case 3: - v4l_info(client, "Detected format: SECAM\n"); - break; - default: - v4l_info(client, "Detected format: BW/No color\n"); - break; - } - v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height); } /* setup the sliced VBI lcr registers according to the sliced VBI format */ -static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt) +static void saa711x_set_lcr(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); int is_50hz = (state->std & V4L2_STD_625_50); u8 lcr[24]; int i, x; #if 1 /* keep */ /* saa7113/7114/7118 VBI support are experimental */ - if (!saa711x_has_reg(state->ident,R_41_LCR_BASE)) + if (!saa711x_has_reg(state->ident, R_41_LCR_BASE)) return; #else @@ -1110,16 +1064,16 @@ static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo /* write the lcr registers */ for (i = 2; i <= 23; i++) { - saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]); + saa711x_write(sd, i - 2 + R_41_LCR_BASE, lcr[i]); } /* enable/disable raw VBI capturing */ - saa711x_writeregs(client, fmt == NULL ? + saa711x_writeregs(sd, fmt == NULL ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off); } -static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ @@ -1135,10 +1089,10 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ - if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10) + if (saa711x_read(sd, R_80_GLOBAL_CNTL_1) & 0x10) return 0; for (i = 2; i <= 23; i++) { - u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE); + u8 v = saa711x_read(sd, i - 2 + R_41_LCR_BASE); sliced->service_lines[0][i] = lcr2vbi[v >> 4]; sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; @@ -1148,20 +1102,20 @@ static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt return 0; } -static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) +static int saa711x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { - saa711x_set_lcr(client, &fmt->fmt.sliced); + saa711x_set_lcr(sd, &fmt->fmt.sliced); return 0; } if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - saa711x_set_lcr(client, NULL); + saa711x_set_lcr(sd, NULL); return 0; } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height); + return saa711x_set_size(sd, fmt->fmt.pix.width, fmt->fmt.pix.height); } /* Decode the sliced VBI data stream as created by the saa7115. @@ -1170,13 +1124,12 @@ static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt The current implementation uses SAV/EAV codes and not the ancillary data headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV code. */ -static void saa711x_decode_vbi_line(struct i2c_client *client, - struct v4l2_decode_vbi_line *vbi) +static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) { + struct saa711x_state *state = to_state(sd); static const char vbi_no_data_pattern[] = { 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 }; - struct saa711x_state *state = i2c_get_clientdata(client); u8 *p = vbi->p; u32 wss; int id1, id2; /* the ID1 and ID2 bytes from the internal header */ @@ -1203,7 +1156,7 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, /* If the VBI slicer does not detect any signal it will fill up the payload buffer with 0xa0 bytes. */ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) - return; + return 0; /* decode payloads */ switch (id2) { @@ -1212,275 +1165,352 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, break; case 4: if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1])) - return; + return 0; vbi->type = V4L2_SLICED_CAPTION_525; break; case 5: wss = saa711x_decode_wss(p); if (wss == -1) - return; + return 0; p[0] = wss & 0xff; p[1] = wss >> 8; vbi->type = V4L2_SLICED_WSS_625; break; case 7: if (saa711x_decode_vps(p, p) != 0) - return; + return 0; vbi->type = V4L2_SLICED_VPS; break; default: - return; + break; } + return 0; } /* ============ SAA7115 AUDIO settings (end) ============= */ -static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int saa711x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) { - struct saa711x_state *state = i2c_get_clientdata(client); + struct saa711x_state *state = to_state(sd); + int status; - /* ioctls to allow direct access to the saa7115 registers for testing */ - switch (cmd) { - case VIDIOC_S_FMT: - return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg); + if (state->radio) + return 0; + status = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - case VIDIOC_G_FMT: - return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg); + v4l2_dbg(1, debug, sd, "status: 0x%02x\n", status); + vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; + return 0; +} - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa711x_set_audio_clock_freq(client, *(u32 *)arg); +static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill_std(qc); + default: + return -EINVAL; + } +} - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - int status; +static int saa711x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa711x_state *state = to_state(sd); - if (state->radio) - break; - status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC); + state->radio = 0; + saa711x_set_v4lstd(sd, std); + return 0; +} - v4l_dbg(1, debug, client, "status: 0x%02x\n", status); - vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; - break; - } +static int saa711x_s_radio(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_LOG_STATUS: - saa711x_log_status(client); - break; + state->radio = 1; + return 0; +} - case VIDIOC_G_CTRL: - return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg); +static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa711x_state *state = to_state(sd); + u32 input = route->input; + u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; + + v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output); + /* saa7111/3 does not have these inputs */ + if ((state->ident == V4L2_IDENT_SAA7113 || + state->ident == V4L2_IDENT_SAA7111) && + (route->input == SAA7115_COMPOSITE4 || + route->input == SAA7115_COMPOSITE5)) { + return -EINVAL; + } + if (route->input > SAA7115_SVIDEO3) + return -EINVAL; + if (route->output > SAA7115_IPORT_ON) + return -EINVAL; + if (state->input == route->input && state->output == route->output) + return 0; + v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", + (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", + (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); + state->input = route->input; + + /* saa7111 has slightly different input numbering */ + if (state->ident == V4L2_IDENT_SAA7111) { + if (input >= SAA7115_COMPOSITE4) + input -= 2; + /* saa7111 specific */ + saa711x_write(sd, R_10_CHROMA_CNTL_2, + (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | + ((route->output & 0xc0) ^ 0x40)); + saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, + (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | + ((route->output & 2) ? 0x0a : 0)); + } - case VIDIOC_S_CTRL: - return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg); + /* select mode */ + saa711x_write(sd, R_02_INPUT_CNTL_1, + (saa711x_read(sd, R_02_INPUT_CNTL_1) & mask) | + input); - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; + /* bypass chrominance trap for S-Video modes */ + saa711x_write(sd, R_09_LUMA_CNTL, + (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | + (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill_std(qc); - default: - return -EINVAL; - } + state->output = route->output; + if (state->ident == V4L2_IDENT_SAA7114 || + state->ident == V4L2_IDENT_SAA7115) { + saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, + (saa711x_read(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | + (state->output & 0x01)); } + return 0; +} - case VIDIOC_G_STD: - *(v4l2_std_id *)arg = saa711x_get_v4lstd(client); - break; +static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_S_STD: - state->radio = 0; - saa711x_set_v4lstd(client, *(v4l2_std_id *)arg); - break; + if (state->ident != V4L2_IDENT_SAA7111) + return -EINVAL; + saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | + (val ? 0x80 : 0)); + return 0; +} - case AUDC_SET_RADIO: - state->radio = 1; - break; +static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa711x_state *state = to_state(sd); - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; + v4l2_dbg(1, debug, sd, "%s output\n", + enable ? "enable" : "disable"); - route->input = state->input; - route->output = state->output; - break; + if (state->enable != enable) { + state->enable = enable; + saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, + state->enable); } + return 0; +} - case VIDIOC_INT_S_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; - u32 input = route->input; - u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; - - v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output); - /* saa7111/3 does not have these inputs */ - if ((state->ident == V4L2_IDENT_SAA7113 || - state->ident == V4L2_IDENT_SAA7111) && - (route->input == SAA7115_COMPOSITE4 || - route->input == SAA7115_COMPOSITE5)) { - return -EINVAL; - } - if (route->input > SAA7115_SVIDEO3) - return -EINVAL; - if (route->output > SAA7115_IPORT_ON) - return -EINVAL; - if (state->input == route->input && state->output == route->output) - break; - v4l_dbg(1, debug, client, "now setting %s input %s output\n", - (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); - state->input = route->input; - - /* saa7111 has slightly different input numbering */ - if (state->ident == V4L2_IDENT_SAA7111) { - if (input >= SAA7115_COMPOSITE4) - input -= 2; - /* saa7111 specific */ - saa711x_write(client, R_10_CHROMA_CNTL_2, - (saa711x_read(client, R_10_CHROMA_CNTL_2) & 0x3f) | - ((route->output & 0xc0) ^ 0x40)); - saa711x_write(client, R_13_RT_X_PORT_OUT_CNTL, - (saa711x_read(client, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | - ((route->output & 2) ? 0x0a : 0)); - } +static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq) +{ + struct saa711x_state *state = to_state(sd); - /* select mode */ - saa711x_write(client, R_02_INPUT_CNTL_1, - (saa711x_read(client, R_02_INPUT_CNTL_1) & mask) | - input); + if (freq->freq != SAA7115_FREQ_32_11_MHZ && + freq->freq != SAA7115_FREQ_24_576_MHZ) + return -EINVAL; + state->crystal_freq = freq->freq; + state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + saa711x_s_clock_freq(sd, state->audclk_freq); + return 0; +} - /* bypass chrominance trap for S-Video modes */ - saa711x_write(client, R_09_LUMA_CNTL, - (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) | - (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); +static int saa711x_reset(struct v4l2_subdev *sd, u32 val) +{ + v4l2_dbg(1, debug, sd, "decoder RESET\n"); + saa711x_writeregs(sd, saa7115_cfg_reset_scaler); + return 0; +} - state->output = route->output; - if (state->ident == V4L2_IDENT_SAA7114 || - state->ident == V4L2_IDENT_SAA7115) { - saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, - (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | - (state->output & 0x01)); +static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *data) +{ + /* Note: the internal field ID is inverted for NTSC, + so data->field 0 maps to the saa7115 even field, + whereas for PAL it maps to the saa7115 odd field. */ + switch (data->id) { + case V4L2_SLICED_WSS_625: + if (saa711x_read(sd, 0x6b) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x6c); + data->data[1] = saa711x_read(sd, 0x6d); + return 0; + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) { + /* CC */ + if (saa711x_read(sd, 0x66) & 0x30) + return -EIO; + data->data[0] = saa711x_read(sd, 0x69); + data->data[1] = saa711x_read(sd, 0x6a); + return 0; } - break; + /* XDS */ + if (saa711x_read(sd, 0x66) & 0xc0) + return -EIO; + data->data[0] = saa711x_read(sd, 0x67); + data->data[1] = saa711x_read(sd, 0x68); + return 0; + default: + return -EINVAL; } +} - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - v4l_dbg(1, debug, client, "%s output\n", - (cmd == VIDIOC_STREAMON) ? "enable" : "disable"); - - if (state->enable != (cmd == VIDIOC_STREAMON)) { - state->enable = (cmd == VIDIOC_STREAMON); - saa711x_write(client, - R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, - state->enable); - } - break; +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_CRYSTAL_FREQ: - { - struct v4l2_crystal_freq *freq = arg; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa711x_read(sd, reg->reg & 0xff); + return 0; +} - if (freq->freq != SAA7115_FREQ_32_11_MHZ && - freq->freq != SAA7115_FREQ_24_576_MHZ) - return -EINVAL; - state->crystal_freq = freq->freq; - state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; - saa711x_set_audio_clock_freq(client, state->audclk_freq); - break; - } +static int saa711x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_DECODE_VBI_LINE: - saa711x_decode_vbi_line(client, arg); - break; + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa711x_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif - case VIDIOC_INT_RESET: - v4l_dbg(1, debug, client, "decoder RESET\n"); - saa711x_writeregs(client, saa7115_cfg_reset_scaler); - break; +static int saa711x_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct saa711x_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_INT_S_GPIO: - if (state->ident != V4L2_IDENT_SAA7111) - return -EINVAL; - saa711x_write(client, 0x11, (saa711x_read(client, 0x11) & 0x7f) | - (*(u32 *)arg ? 0x80 : 0)); - break; + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} - case VIDIOC_INT_G_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; +static int saa711x_log_status(struct v4l2_subdev *sd) +{ + struct saa711x_state *state = to_state(sd); + int reg1e, reg1f; + int signalOk; + int vcr; - /* Note: the internal field ID is inverted for NTSC, - so data->field 0 maps to the saa7115 even field, - whereas for PAL it maps to the saa7115 odd field. */ - switch (data->id) { - case V4L2_SLICED_WSS_625: - if (saa711x_read(client, 0x6b) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x6c); - data->data[1] = saa711x_read(client, 0x6d); - return 0; - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) { - /* CC */ - if (saa711x_read(client, 0x66) & 0x30) - return -EIO; - data->data[0] = saa711x_read(client, 0x69); - data->data[1] = saa711x_read(client, 0x6a); - return 0; - } - /* XDS */ - if (saa711x_read(client, 0x66) & 0xc0) - return -EIO; - data->data[0] = saa711x_read(client, 0x67); - data->data[1] = saa711x_read(client, 0x68); - return 0; - default: - return -EINVAL; - } - break; + v4l2_info(sd, "Audio frequency: %d Hz\n", state->audclk_freq); + if (state->ident != V4L2_IDENT_SAA7115) { + /* status for the saa7114 */ + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); + signalOk = (reg1f & 0xc1) == 0x81; + v4l2_info(sd, "Video signal: %s\n", signalOk ? "ok" : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + return 0; } -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; + /* status for the saa7115 */ + reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC); + reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC); - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = saa711x_read(client, reg->reg & 0xff); - else - saa711x_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif + signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; + vcr = !(reg1f & 0x10); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); + if (state->input >= 6) + v4l2_info(sd, "Input: S-Video %d\n", state->input - 6); + else + v4l2_info(sd, "Input: Composite %d\n", state->input); + v4l2_info(sd, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); + v4l2_info(sd, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); + switch (reg1e & 0x03) { + case 1: + v4l2_info(sd, "Detected format: NTSC\n"); + break; + case 2: + v4l2_info(sd, "Detected format: PAL\n"); + break; + case 3: + v4l2_info(sd, "Detected format: SECAM\n"); + break; default: - return -EINVAL; + v4l2_info(sd, "Detected format: BW/No color\n"); + break; } - + v4l2_info(sd, "Width, Height: %d, %d\n", state->width, state->height); return 0; } +static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa711x_core_ops = { + .log_status = saa711x_log_status, + .g_chip_ident = saa711x_g_chip_ident, + .g_ctrl = saa711x_g_ctrl, + .s_ctrl = saa711x_s_ctrl, + .queryctrl = saa711x_queryctrl, + .reset = saa711x_reset, + .s_gpio = saa711x_s_gpio, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa711x_g_register, + .s_register = saa711x_s_register, +#endif +}; + +static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { + .s_std = saa711x_s_std, + .s_radio = saa711x_s_radio, + .g_tuner = saa711x_g_tuner, +}; + +static const struct v4l2_subdev_audio_ops saa711x_audio_ops = { + .s_clock_freq = saa711x_s_clock_freq, +}; + +static const struct v4l2_subdev_video_ops saa711x_video_ops = { + .s_routing = saa711x_s_routing, + .s_crystal_freq = saa711x_s_crystal_freq, + .g_fmt = saa711x_g_fmt, + .s_fmt = saa711x_s_fmt, + .g_vbi_data = saa711x_g_vbi_data, + .decode_vbi_line = saa711x_decode_vbi_line, + .s_stream = saa711x_s_stream, +}; + +static const struct v4l2_subdev_ops saa711x_ops = { + .core = &saa711x_core_ops, + .tuner = &saa711x_tuner_ops, + .audio = &saa711x_audio_ops, + .video = &saa711x_video_ops, +}; + /* ----------------------------------------------------------------------- */ -static int saa7115_probe(struct i2c_client *client, +static int saa711x_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct saa711x_state *state; + struct v4l2_subdev *sd; int i; char name[17]; char chip_id; @@ -1497,8 +1527,8 @@ static int saa7115_probe(struct i2c_client *client, #endif for (i = 0; i < 0x0f; i++) { - saa711x_write(client, 0, i); - name[i] = (saa711x_read(client, 0) & 0x0f) + '0'; + i2c_smbus_write_byte_data(client, 0, i); + name[i] = (i2c_smbus_read_byte_data(client, 0) & 0x0f) + '0'; if (name[i] > '9') name[i] += 'a' - '9' - 1; } @@ -1527,7 +1557,8 @@ static int saa7115_probe(struct i2c_client *client, state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa711x_ops); state->input = -1; state->output = SAA7115_IPORT_ON; state->enable = 1; @@ -1554,41 +1585,45 @@ static int saa7115_probe(struct i2c_client *client, break; default: state->ident = V4L2_IDENT_SAA7111; - v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n"); + v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); } state->audclk_freq = 48000; - v4l_dbg(1, debug, client, "writing init values\n"); + v4l2_dbg(1, debug, sd, "writing init values\n"); /* init to 60hz/48khz */ state->crystal_freq = SAA7115_FREQ_24_576_MHZ; switch (state->ident) { case V4L2_IDENT_SAA7111: - saa711x_writeregs(client, saa7111_init); + saa711x_writeregs(sd, saa7111_init); break; case V4L2_IDENT_SAA7113: - saa711x_writeregs(client, saa7113_init); + saa711x_writeregs(sd, saa7113_init); break; default: state->crystal_freq = SAA7115_FREQ_32_11_MHZ; - saa711x_writeregs(client, saa7115_init_auto_input); + saa711x_writeregs(sd, saa7115_init_auto_input); } if (state->ident != V4L2_IDENT_SAA7111) - saa711x_writeregs(client, saa7115_init_misc); - saa711x_set_v4lstd(client, V4L2_STD_NTSC); + saa711x_writeregs(sd, saa7115_init_misc); + saa711x_set_v4lstd(sd, V4L2_STD_NTSC); - v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n", - saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC)); + v4l2_dbg(1, debug, sd, "status: (1E) 0x%02x, (1F) 0x%02x\n", + saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC), + saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC)); return 0; } /* ----------------------------------------------------------------------- */ -static int saa7115_remove(struct i2c_client *client) +static int saa711x_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } @@ -1608,9 +1643,9 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7115", .driverid = I2C_DRIVERID_SAA711X, - .command = saa7115_command, - .probe = saa7115_probe, - .remove = saa7115_remove, + .command = saa711x_command, + .probe = saa711x_probe, + .remove = saa711x_remove, .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = saa7115_id, diff --git a/linux/drivers/media/video/saa7127.c b/linux/drivers/media/video/saa7127.c index fd7d15305..5414b027f 100644 --- a/linux/drivers/media/video/saa7127.c +++ b/linux/drivers/media/video/saa7127.c @@ -53,7 +53,7 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/saa7127.h> @@ -237,6 +237,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = { */ struct saa7127_state { + struct v4l2_subdev sd; v4l2_std_id std; u32 ident; enum saa7127_input_type input_type; @@ -256,6 +257,11 @@ struct saa7127_state { u8 reg_61; }; +static inline struct saa7127_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7127_state, sd); +} + static const char * const output_strs[] = { "S-Video + Composite", @@ -287,32 +293,35 @@ static const char * const wss_strs[] = { /* ----------------------------------------------------------------------- */ -static int saa7127_read(struct i2c_client *client, u8 reg) +static int saa7127_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + return i2c_smbus_read_byte_data(client, reg); } /* ----------------------------------------------------------------------- */ -static int saa7127_write(struct i2c_client *client, u8 reg, u8 val) +static int saa7127_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; for (i = 0; i < 3; i++) { if (i2c_smbus_write_byte_data(client, reg, val) == 0) return 0; } - v4l_err(client, "I2C Write Problem\n"); + v4l2_err(sd, "I2C Write Problem\n"); return -1; } /* ----------------------------------------------------------------------- */ -static int saa7127_write_inittab(struct i2c_client *client, +static int saa7127_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs) { while (regs->reg != 0) { - saa7127_write(client, regs->reg, regs->value); + saa7127_write(sd, regs->reg, regs->value); regs++; } return 0; @@ -320,16 +329,16 @@ static int saa7127_write_inittab(struct i2c_client *client, /* ----------------------------------------------------------------------- */ -static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_vps(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 16)) return -EINVAL; if (state->vps_enable != enable) { - v4l_dbg(1, debug, client, "Turn VPS Signal %s\n", enable ? "on" : "off"); - saa7127_write(client, 0x54, enable << 7); + v4l2_dbg(1, debug, sd, "Turn VPS Signal %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x54, enable << 7); state->vps_enable = enable; } if (!enable) @@ -340,91 +349,91 @@ static int saa7127_set_vps(struct i2c_client *client, struct v4l2_sliced_vbi_dat state->vps_data[2] = data->data[9]; state->vps_data[3] = data->data[10]; state->vps_data[4] = data->data[11]; - v4l_dbg(1, debug, client, "Set VPS data %02x %02x %02x %02x %02x\n", + v4l2_dbg(1, debug, sd, "Set VPS data %02x %02x %02x %02x %02x\n", state->vps_data[0], state->vps_data[1], state->vps_data[2], state->vps_data[3], state->vps_data[4]); - saa7127_write(client, 0x55, state->vps_data[0]); - saa7127_write(client, 0x56, state->vps_data[1]); - saa7127_write(client, 0x57, state->vps_data[2]); - saa7127_write(client, 0x58, state->vps_data[3]); - saa7127_write(client, 0x59, state->vps_data[4]); + saa7127_write(sd, 0x55, state->vps_data[0]); + saa7127_write(sd, 0x56, state->vps_data[1]); + saa7127_write(sd, 0x57, state->vps_data[2]); + saa7127_write(sd, 0x58, state->vps_data[3]); + saa7127_write(sd, 0x59, state->vps_data[4]); return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_cc(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_cc(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); u16 cc = data->data[1] << 8 | data->data[0]; int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 21)) return -EINVAL; if (state->cc_enable != enable) { - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "Turn CC %s\n", enable ? "on" : "off"); - saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, (state->xds_enable << 7) | (enable << 6) | 0x11); state->cc_enable = enable; } if (!enable) return 0; - v4l_dbg(2, debug, client, "CC data: %04x\n", cc); - saa7127_write(client, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); - saa7127_write(client, SAA7127_REG_LINE_21_ODD_1, cc >> 8); + v4l2_dbg(2, debug, sd, "CC data: %04x\n", cc); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_0, cc & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_ODD_1, cc >> 8); state->cc_data = cc; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_xds(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_xds(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); u16 xds = data->data[1] << 8 | data->data[0]; int enable = (data->line != 0); if (enable && (data->field != 1 || data->line != 21)) return -EINVAL; if (state->xds_enable != enable) { - v4l_dbg(1, debug, client, "Turn XDS %s\n", enable ? "on" : "off"); - saa7127_write(client, SAA7127_REG_CLOSED_CAPTION, + v4l2_dbg(1, debug, sd, "Turn XDS %s\n", enable ? "on" : "off"); + saa7127_write(sd, SAA7127_REG_CLOSED_CAPTION, (enable << 7) | (state->cc_enable << 6) | 0x11); state->xds_enable = enable; } if (!enable) return 0; - v4l_dbg(2, debug, client, "XDS data: %04x\n", xds); - saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); - saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); + v4l2_dbg(2, debug, sd, "XDS data: %04x\n", xds); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); + saa7127_write(sd, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); state->xds_data = xds; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data) +static int saa7127_set_wss(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 23)) return -EINVAL; if (state->wss_enable != enable) { - v4l_dbg(1, debug, client, "Turn WSS %s\n", enable ? "on" : "off"); - saa7127_write(client, 0x27, enable << 7); + v4l2_dbg(1, debug, sd, "Turn WSS %s\n", enable ? "on" : "off"); + saa7127_write(sd, 0x27, enable << 7); state->wss_enable = enable; } if (!enable) return 0; - saa7127_write(client, 0x26, data->data[0]); - saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f)); - v4l_dbg(1, debug, client, + saa7127_write(sd, 0x26, data->data[0]); + saa7127_write(sd, 0x27, 0x80 | (data->data[1] & 0x3f)); + v4l2_dbg(1, debug, sd, "WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; return 0; @@ -432,18 +441,18 @@ static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_dat /* ----------------------------------------------------------------------- */ -static int saa7127_set_video_enable(struct i2c_client *client, int enable) +static int saa7127_set_video_enable(struct v4l2_subdev *sd, int enable) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); if (enable) { - v4l_dbg(1, debug, client, "Enable Video Output\n"); - saa7127_write(client, 0x2d, state->reg_2d); - saa7127_write(client, 0x61, state->reg_61); + v4l2_dbg(1, debug, sd, "Enable Video Output\n"); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x61, state->reg_61); } else { - v4l_dbg(1, debug, client, "Disable Video Output\n"); - saa7127_write(client, 0x2d, (state->reg_2d & 0xf0)); - saa7127_write(client, 0x61, (state->reg_61 | 0xc0)); + v4l2_dbg(1, debug, sd, "Disable Video Output\n"); + saa7127_write(sd, 0x2d, (state->reg_2d & 0xf0)); + saa7127_write(sd, 0x61, (state->reg_61 | 0xc0)); } state->video_enable = enable; return 0; @@ -451,32 +460,32 @@ static int saa7127_set_video_enable(struct i2c_client *client, int enable) /* ----------------------------------------------------------------------- */ -static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std) +static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); const struct i2c_reg_value *inittab; if (std & V4L2_STD_525_60) { - v4l_dbg(1, debug, client, "Selecting 60 Hz video Standard\n"); + v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); inittab = saa7127_init_config_60hz; state->reg_61 = SAA7127_60HZ_DAC_CONTROL; } else { - v4l_dbg(1, debug, client, "Selecting 50 Hz video Standard\n"); + v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n"); inittab = saa7127_init_config_50hz; state->reg_61 = SAA7127_50HZ_DAC_CONTROL; } /* Write Table */ - saa7127_write_inittab(client, inittab); + saa7127_write_inittab(sd, inittab); state->std = std; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_output_type(struct i2c_client *client, int output) +static int saa7127_set_output_type(struct v4l2_subdev *sd, int output) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); switch (output) { case SAA7127_OUTPUT_TYPE_RGB: @@ -512,165 +521,195 @@ static int saa7127_set_output_type(struct i2c_client *client, int output) default: return -EINVAL; } - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "Selecting %s output type\n", output_strs[output]); /* Configure Encoder */ - saa7127_write(client, 0x2d, state->reg_2d); - saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + saa7127_write(sd, 0x2d, state->reg_2d); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); state->output_type = output; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_set_input_type(struct i2c_client *client, int input) +static int saa7127_set_input_type(struct v4l2_subdev *sd, int input) { - struct saa7127_state *state = i2c_get_clientdata(client); + struct saa7127_state *state = to_state(sd); switch (input) { case SAA7127_INPUT_TYPE_NORMAL: /* avia */ - v4l_dbg(1, debug, client, "Selecting Normal Encoder Input\n"); + v4l2_dbg(1, debug, sd, "Selecting Normal Encoder Input\n"); state->reg_3a_cb = 0; break; case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ - v4l_dbg(1, debug, client, "Selecting Color Bar generator\n"); + v4l2_dbg(1, debug, sd, "Selecting Color Bar generator\n"); state->reg_3a_cb = 0x80; break; default: return -EINVAL; } - saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); + saa7127_write(sd, 0x3a, state->reg_3a | state->reg_3a_cb); state->input_type = input; return 0; } /* ----------------------------------------------------------------------- */ -static int saa7127_command(struct i2c_client *client, - unsigned int cmd, void *arg) +static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) { - struct saa7127_state *state = i2c_get_clientdata(client); - struct v4l2_format *fmt = arg; - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_INT_S_STD_OUTPUT: - if (state->std == *(v4l2_std_id *)arg) - break; - return saa7127_set_std(client, *(v4l2_std_id *)arg); - - case VIDIOC_INT_G_STD_OUTPUT: - *(v4l2_std_id *)arg = state->std; - break; + struct saa7127_state *state = to_state(sd); - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = state->input_type; - route->output = state->output_type; - break; + if (state->std == std) + return 0; + return saa7127_set_std(sd, std); +} - case VIDIOC_INT_S_VIDEO_ROUTING: - { - int rc = 0; +static int saa7127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa7127_state *state = to_state(sd); + int rc = 0; + + if (state->input_type != route->input) + rc = saa7127_set_input_type(sd, route->input); + if (rc == 0 && state->output_type != route->output) + rc = saa7127_set_output_type(sd, route->output); + return rc; +} - if (state->input_type != route->input) - rc = saa7127_set_input_type(client, route->input); - if (rc == 0 && state->output_type != route->output) - rc = saa7127_set_output_type(client, route->output); - return rc; - } +static int saa7127_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa7127_state *state = to_state(sd); - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - if (state->video_enable == (cmd == VIDIOC_STREAMON)) - break; - return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON); - - case VIDIOC_G_FMT: - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - - memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); - if (state->vps_enable) - fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; - if (state->wss_enable) - fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; - if (state->cc_enable) { - fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; - fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; - } - fmt->fmt.sliced.service_set = - (state->vps_enable ? V4L2_SLICED_VPS : 0) | - (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | - (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); - break; + if (state->video_enable == enable) + return 0; + return saa7127_set_video_enable(sd, enable); +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); - v4l_info(client, "Input: %s\n", state->input_type ? "color bars" : "normal"); - v4l_info(client, "Output: %s\n", state->video_enable ? - output_strs[state->output_type] : "disabled"); - v4l_info(client, "WSS: %s\n", state->wss_enable ? - wss_strs[state->wss_mode] : "disabled"); - v4l_info(client, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); - v4l_info(client, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); - break; +static int saa7127_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct saa7127_state *state = to_state(sd); -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) - reg->val = saa7127_read(client, reg->reg & 0xff); - else - saa7127_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } -#endif + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; - case VIDIOC_INT_S_VBI_DATA: - { - struct v4l2_sliced_vbi_data *data = arg; - - switch (data->id) { - case V4L2_SLICED_WSS_625: - return saa7127_set_wss(client, data); - case V4L2_SLICED_VPS: - return saa7127_set_vps(client, data); - case V4L2_SLICED_CAPTION_525: - if (data->field == 0) - return saa7127_set_cc(client, data); - return saa7127_set_xds(client, data); - default: - return -EINVAL; - } - break; + memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); + if (state->vps_enable) + fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; + if (state->wss_enable) + fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; + if (state->cc_enable) { + fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; + fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; } + fmt->fmt.sliced.service_set = + (state->vps_enable ? V4L2_SLICED_VPS : 0) | + (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | + (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); + return 0; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); - +static int saa7127_s_vbi_data(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *data) +{ + switch (data->id) { + case V4L2_SLICED_WSS_625: + return saa7127_set_wss(sd, data); + case V4L2_SLICED_VPS: + return saa7127_set_vps(sd, data); + case V4L2_SLICED_CAPTION_525: + if (data->field == 0) + return saa7127_set_cc(sd, data); + return saa7127_set_xds(sd, data); default: return -EINVAL; } return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa7127_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa7127_read(sd, reg->reg & 0xff); + return 0; +} + +static int saa7127_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa7127_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + +static int saa7127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct saa7127_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, state->ident, 0); +} + +static int saa7127_log_status(struct v4l2_subdev *sd) +{ + struct saa7127_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); + v4l2_info(sd, "Input: %s\n", state->input_type ? "color bars" : "normal"); + v4l2_info(sd, "Output: %s\n", state->video_enable ? + output_strs[state->output_type] : "disabled"); + v4l2_info(sd, "WSS: %s\n", state->wss_enable ? + wss_strs[state->wss_mode] : "disabled"); + v4l2_info(sd, "VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); + v4l2_info(sd, "CC: %s\n", state->cc_enable ? "enabled" : "disabled"); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa7127_core_ops = { + .log_status = saa7127_log_status, + .g_chip_ident = saa7127_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa7127_g_register, + .s_register = saa7127_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops saa7127_video_ops = { + .s_vbi_data = saa7127_s_vbi_data, + .g_fmt = saa7127_g_fmt, + .s_std_output = saa7127_s_std_output, + .s_routing = saa7127_s_routing, + .s_stream = saa7127_s_stream, +}; + +static const struct v4l2_subdev_ops saa7127_ops = { + .core = &saa7127_core_ops, + .video = &saa7127_video_ops, +}; + /* ----------------------------------------------------------------------- */ static int saa7127_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct saa7127_state *state; + struct v4l2_subdev *sd; struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ /* Check if the adapter supports the needed features */ @@ -684,40 +723,42 @@ static int saa7127_probe(struct i2c_client *client, v4l_dbg(1, debug, client, "detecting saa7127 client on address 0x%x\n", client->addr << 1); + state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa7127_ops); + /* First test register 0: Bits 5-7 are a version ID (should be 0), and bit 2 should also be 0. This is rather general, so the second test is more specific and looks at the 'ending point of burst in clock cycles' which is 0x1d after a reset and not expected to ever change. */ - if ((saa7127_read(client, 0) & 0xe4) != 0 || - (saa7127_read(client, 0x29) & 0x3f) != 0x1d) { - v4l_dbg(1, debug, client, "saa7127 not found\n"); + if ((saa7127_read(sd, 0) & 0xe4) != 0 || + (saa7127_read(sd, 0x29) & 0x3f) != 0x1d) { + v4l2_dbg(1, debug, sd, "saa7127 not found\n"); + kfree(state); return -ENODEV; } - state = kzalloc(sizeof(struct saa7127_state), GFP_KERNEL); - - if (state == NULL) - return -ENOMEM; - - i2c_set_clientdata(client, state); /* Configure Encoder */ - v4l_dbg(1, debug, client, "Configuring encoder\n"); - saa7127_write_inittab(client, saa7127_init_config_common); - saa7127_set_std(client, V4L2_STD_NTSC); - saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH); - saa7127_set_vps(client, &vbi); - saa7127_set_wss(client, &vbi); - saa7127_set_cc(client, &vbi); - saa7127_set_xds(client, &vbi); + v4l2_dbg(1, debug, sd, "Configuring encoder\n"); + saa7127_write_inittab(sd, saa7127_init_config_common); + saa7127_set_std(sd, V4L2_STD_NTSC); + saa7127_set_output_type(sd, SAA7127_OUTPUT_TYPE_BOTH); + saa7127_set_vps(sd, &vbi); + saa7127_set_wss(sd, &vbi); + saa7127_set_cc(sd, &vbi); + saa7127_set_xds(sd, &vbi); if (test_image == 1) /* The Encoder has an internal Colorbar generator */ /* This can be used for debugging */ - saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE); + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_TEST_IMAGE); else - saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL); - saa7127_set_video_enable(client, 1); + saa7127_set_input_type(sd, SAA7127_INPUT_TYPE_NORMAL); + saa7127_set_video_enable(sd, 1); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) if (id->driver_data) { /* Chip type is already known */ @@ -729,10 +770,10 @@ static int saa7127_probe(struct i2c_client *client, int read_result; /* Detect if it's an saa7129 */ - read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2); - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa); - if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { - saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, + read_result = saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2); + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, 0xaa); + if (saa7127_read(sd, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { + saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, read_result); state->ident = V4L2_IDENT_SAA7129; strlcpy(client->name, "saa7129", I2C_NAME_SIZE); @@ -742,10 +783,10 @@ static int saa7127_probe(struct i2c_client *client, } } - v4l_info(client, "%s found @ 0x%x (%s)\n", client->name, + v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, client->addr << 1, client->adapter->name); if (state->ident == V4L2_IDENT_SAA7129) - saa7127_write_inittab(client, saa7129_init_config_extra); + saa7127_write_inittab(sd, saa7129_init_config_extra); return 0; } @@ -753,9 +794,12 @@ static int saa7127_probe(struct i2c_client *client, static int saa7127_remove(struct i2c_client *client) { + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); /* Turn off TV output */ - saa7127_set_video_enable(client, 0); - kfree(i2c_get_clientdata(client)); + saa7127_set_video_enable(sd, 0); + kfree(to_state(sd)); return 0; } @@ -776,7 +820,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7127", .driverid = I2C_DRIVERID_SAA7127, - .command = saa7127_command, .probe = saa7127_probe, .remove = saa7127_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/saa717x.c b/linux/drivers/media/video/saa717x.c index b0751c195..2d667e66a 100644 --- a/linux/drivers/media/video/saa717x.c +++ b/linux/drivers/media/video/saa717x.c @@ -37,7 +37,7 @@ #include <linux/videodev2.h> #include <linux/i2c.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-i2c-drv.h> #include "compat.h" @@ -60,6 +60,7 @@ I2C_CLIENT_INSMOD; #endif struct saa717x_state { + struct v4l2_subdev sd; v4l2_std_id std; int input; int enable; @@ -81,6 +82,11 @@ struct saa717x_state { int audio_input; }; +static inline struct saa717x_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa717x_state, sd); +} + /* ----------------------------------------------------------------------- */ /* for audio mode */ @@ -94,8 +100,9 @@ struct saa717x_state { /* ----------------------------------------------------------------------- */ -static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) +static int saa717x_write(struct v4l2_subdev *sd, u32 reg, u32 value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct i2c_adapter *adap = client->adapter; int fw_addr = reg == 0x454 || (reg >= 0x464 && reg <= 0x478) || reg == 0x480 || reg == 0x488; unsigned char mm1[6]; @@ -115,20 +122,21 @@ static int saa717x_write(struct i2c_client *client, u32 reg, u32 value) } msg.len = fw_addr ? 5 : 3; /* Long Registers have *only* three bytes! */ msg.buf = mm1; - v4l_dbg(2, debug, client, "wrote: reg 0x%03x=%08x\n", reg, value); + v4l2_dbg(2, debug, sd, "wrote: reg 0x%03x=%08x\n", reg, value); return i2c_transfer(adap, &msg, 1) == 1; } -static void saa717x_write_regs(struct i2c_client *client, u32 *data) +static void saa717x_write_regs(struct v4l2_subdev *sd, u32 *data) { while (data[0] || data[1]) { - saa717x_write(client, data[0], data[1]); + saa717x_write(sd, data[0], data[1]); data += 2; } } -static u32 saa717x_read(struct i2c_client *client, u32 reg) +static u32 saa717x_read(struct v4l2_subdev *sd, u32 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct i2c_adapter *adap = client->adapter; int fw_addr = (reg >= 0x404 && reg <= 0x4b8) || reg == 0x528; unsigned char mm1[2]; @@ -152,7 +160,7 @@ static u32 saa717x_read(struct i2c_client *client, u32 reg) else value = mm2[0] & 0xff; - v4l_dbg(2, debug, client, "read: reg 0x%03x=0x%08x\n", reg, value); + v4l2_dbg(2, debug, sd, "read: reg 0x%03x=0x%08x\n", reg, value); return value; } @@ -686,7 +694,7 @@ static u32 reg_set_audio_template[4][2] = /* Get detected audio flags (from saa7134 driver) */ -static void get_inf_dev_status(struct i2c_client *client, +static void get_inf_dev_status(struct v4l2_subdev *sd, int *dual_flag, int *stereo_flag) { u32 reg_data3; @@ -725,13 +733,13 @@ static void get_inf_dev_status(struct i2c_client *client, /* (demdec status: 0x528) */ /* read current status */ - reg_data3 = saa717x_read(client, 0x0528); + reg_data3 = saa717x_read(sd, 0x0528); - v4l_dbg(1, debug, client, "tvaudio thread status: 0x%x [%s%s%s]\n", + v4l2_dbg(1, debug, sd, "tvaudio thread status: 0x%x [%s%s%s]\n", reg_data3, stdres[reg_data3 & 0x1f], (reg_data3 & 0x000020) ? ",stereo" : "", (reg_data3 & 0x000040) ? ",dual" : ""); - v4l_dbg(1, debug, client, "detailed status: " + v4l2_dbg(1, debug, sd, "detailed status: " "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", (reg_data3 & 0x000080) ? " A2/EIAJ pilot tone " : "", (reg_data3 & 0x000100) ? " A2/EIAJ dual " : "", @@ -752,51 +760,51 @@ static void get_inf_dev_status(struct i2c_client *client, (reg_data3 & 0x100000) ? " init done " : ""); if (reg_data3 & 0x000220) { - v4l_dbg(1, debug, client, "ST!!!\n"); + v4l2_dbg(1, debug, sd, "ST!!!\n"); *stereo_flag = 1; } if (reg_data3 & 0x000140) { - v4l_dbg(1, debug, client, "DUAL!!!\n"); + v4l2_dbg(1, debug, sd, "DUAL!!!\n"); *dual_flag = 1; } } /* regs write to set audio mode */ -static void set_audio_mode(struct i2c_client *client, int audio_mode) +static void set_audio_mode(struct v4l2_subdev *sd, int audio_mode) { - v4l_dbg(1, debug, client, "writing registers to set audio mode by set %d\n", + v4l2_dbg(1, debug, sd, "writing registers to set audio mode by set %d\n", audio_mode); - saa717x_write(client, 0x46c, reg_set_audio_template[audio_mode][0]); - saa717x_write(client, 0x470, reg_set_audio_template[audio_mode][1]); + saa717x_write(sd, 0x46c, reg_set_audio_template[audio_mode][0]); + saa717x_write(sd, 0x470, reg_set_audio_template[audio_mode][1]); } /* write regs to video output level (bright,contrast,hue,sat) */ -static void set_video_output_level_regs(struct i2c_client *client, +static void set_video_output_level_regs(struct v4l2_subdev *sd, struct saa717x_state *decoder) { /* brightness ffh (bright) - 80h (ITU level) - 00h (dark) */ - saa717x_write(client, 0x10a, decoder->bright); + saa717x_write(sd, 0x10a, decoder->bright); /* contrast 7fh (max: 1.984) - 44h (ITU) - 40h (1.0) - 0h (luminance off) 40: i2c dump c0h (-1.0 inverse chrominance) 80h (-2.0 inverse chrominance) */ - saa717x_write(client, 0x10b, decoder->contrast); + saa717x_write(sd, 0x10b, decoder->contrast); /* saturation? 7fh(max)-40h(ITU)-0h(color off) c0h (-1.0 inverse chrominance) 80h (-2.0 inverse chrominance) */ - saa717x_write(client, 0x10c, decoder->sat); + saa717x_write(sd, 0x10c, decoder->sat); /* color hue (phase) control 7fh (+178.6) - 0h (0 normal) - 80h (-180.0) */ - saa717x_write(client, 0x10d, decoder->hue); + saa717x_write(sd, 0x10d, decoder->hue); } /* write regs to set audio volume, bass and treble */ -static int set_audio_regs(struct i2c_client *client, +static int set_audio_regs(struct v4l2_subdev *sd, struct saa717x_state *decoder) { u8 mute = 0xac; /* -84 dB */ @@ -804,8 +812,8 @@ static int set_audio_regs(struct i2c_client *client, unsigned int work_l, work_r; /* set SIF analog I/O select */ - saa717x_write(client, 0x0594, decoder->audio_input); - v4l_dbg(1, debug, client, "set audio input %d\n", + saa717x_write(sd, 0x0594, decoder->audio_input); + v4l2_dbg(1, debug, sd, "set audio input %d\n", decoder->audio_input); /* normalize ( 65535 to 0 -> 24 to -40 (not -84)) */ @@ -825,17 +833,17 @@ static int set_audio_regs(struct i2c_client *client, ((u8)decoder->audio_main_vol_r << 8); } - saa717x_write(client, 0x480, val); + saa717x_write(sd, 0x480, val); /* bass and treble; go to another function */ /* set bass and treble */ val = decoder->audio_main_bass | (decoder->audio_main_treble << 8); - saa717x_write(client, 0x488, val); + saa717x_write(sd, 0x488, val); return 0; } /********** scaling staff ***********/ -static void set_h_prescale(struct i2c_client *client, +static void set_h_prescale(struct v4l2_subdev *sd, int task, int prescale) { static const struct { @@ -868,107 +876,101 @@ static void set_h_prescale(struct i2c_client *client, return; /* horizonal prescaling */ - saa717x_write(client, 0x60 + task_shift, vals[i].xpsc); + saa717x_write(sd, 0x60 + task_shift, vals[i].xpsc); /* accumulation length */ - saa717x_write(client, 0x61 + task_shift, vals[i].xacl); + saa717x_write(sd, 0x61 + task_shift, vals[i].xacl); /* level control */ - saa717x_write(client, 0x62 + task_shift, + saa717x_write(sd, 0x62 + task_shift, (vals[i].xc2_1 << 3) | vals[i].xdcg); /*FIR prefilter control */ - saa717x_write(client, 0x63 + task_shift, + saa717x_write(sd, 0x63 + task_shift, (vals[i].vpfy << 2) | vals[i].vpfy); } /********** scaling staff ***********/ -static void set_v_scale(struct i2c_client *client, int task, int yscale) +static void set_v_scale(struct v4l2_subdev *sd, int task, int yscale) { int task_shift; task_shift = task * 0x40; /* Vertical scaling ratio (LOW) */ - saa717x_write(client, 0x70 + task_shift, yscale & 0xff); + saa717x_write(sd, 0x70 + task_shift, yscale & 0xff); /* Vertical scaling ratio (HI) */ - saa717x_write(client, 0x71 + task_shift, yscale >> 8); -} - -static int saa717x_set_audio_clock_freq(struct i2c_client *client, u32 freq) -{ - /* not yet implament, so saa717x_cfg_??hz_??_audio is not defined. */ - return 0; + saa717x_write(sd, 0x71 + task_shift, yscale >> 8); } -static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa717x_state *state = i2c_get_clientdata(client); + struct saa717x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { - v4l_err(client, "invalid brightness setting %d\n", ctrl->value); + v4l2_err(sd, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; - v4l_dbg(1, debug, client, "bright:%d\n", state->bright); - saa717x_write(client, 0x10a, state->bright); + v4l2_dbg(1, debug, sd, "bright:%d\n", state->bright); + saa717x_write(sd, 0x10a, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid contrast setting %d\n", ctrl->value); + v4l2_err(sd, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; - v4l_dbg(1, debug, client, "contrast:%d\n", state->contrast); - saa717x_write(client, 0x10b, state->contrast); + v4l2_dbg(1, debug, sd, "contrast:%d\n", state->contrast); + saa717x_write(sd, 0x10b, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { - v4l_err(client, "invalid saturation setting %d\n", ctrl->value); + v4l2_err(sd, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; - v4l_dbg(1, debug, client, "sat:%d\n", state->sat); - saa717x_write(client, 0x10c, state->sat); + v4l2_dbg(1, debug, sd, "sat:%d\n", state->sat); + saa717x_write(sd, 0x10c, state->sat); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { - v4l_err(client, "invalid hue setting %d\n", ctrl->value); + v4l2_err(sd, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; - v4l_dbg(1, debug, client, "hue:%d\n", state->hue); - saa717x_write(client, 0x10d, state->hue); + v4l2_dbg(1, debug, sd, "hue:%d\n", state->hue); + saa717x_write(sd, 0x10d, state->hue); break; case V4L2_CID_AUDIO_MUTE: state->audio_main_mute = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_VOLUME: state->audio_main_volume = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_BALANCE: state->audio_main_balance = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_TREBLE: state->audio_main_treble = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; case V4L2_CID_AUDIO_BASS: state->audio_main_bass = ctrl->value; - set_audio_regs(client, state); + set_audio_regs(sd, state); break; default: @@ -978,9 +980,9 @@ static int saa717x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c return 0; } -static int saa717x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int saa717x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct saa717x_state *state = i2c_get_clientdata(client); + struct saa717x_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -1109,13 +1111,15 @@ static struct v4l2_queryctrl saa717x_qctrl[] = { }, }; -static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_state *decoder, int inp) +static int saa717x_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { + struct saa717x_state *decoder = to_state(sd); + int inp = route->input; int is_tuner = inp & 0x80; /* tuner input flag */ inp &= 0x7f; - v4l_dbg(1, debug, client, "decoder set input (%d)\n", inp); + v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", inp); /* inputs from 0-9 are available*/ /* saa717x have mode0-mode9 but mode5 is reserved. */ if (inp < 0 || inp > 9 || inp == 5) @@ -1125,222 +1129,197 @@ static int saa717x_set_video_input(struct i2c_client *client, struct saa717x_sta int input_line = inp; decoder->input = input_line; - v4l_dbg(1, debug, client, "now setting %s input %d\n", + v4l2_dbg(1, debug, sd, "now setting %s input %d\n", input_line >= 6 ? "S-Video" : "Composite", input_line); /* select mode */ - saa717x_write(client, 0x102, - (saa717x_read(client, 0x102) & 0xf0) | + saa717x_write(sd, 0x102, + (saa717x_read(sd, 0x102) & 0xf0) | input_line); /* bypass chrominance trap for modes 6..9 */ - saa717x_write(client, 0x109, - (saa717x_read(client, 0x109) & 0x7f) | + saa717x_write(sd, 0x109, + (saa717x_read(sd, 0x109) & 0x7f) | (input_line < 6 ? 0x0 : 0x80)); /* change audio_mode */ if (is_tuner) { /* tuner */ - set_audio_mode(client, decoder->tuner_audio_mode); + set_audio_mode(sd, decoder->tuner_audio_mode); } else { /* Force to STEREO mode if Composite or * S-Video were chosen */ - set_audio_mode(client, TUNER_AUDIO_STEREO); + set_audio_mode(sd, TUNER_AUDIO_STEREO); } /* change initialize procedure (Composite/S-Video) */ if (is_tuner) - saa717x_write_regs(client, reg_init_tuner_input); + saa717x_write_regs(sd, reg_init_tuner_input); else if (input_line >= 6) - saa717x_write_regs(client, reg_init_svideo_input); + saa717x_write_regs(sd, reg_init_svideo_input); else - saa717x_write_regs(client, reg_init_composite_input); + saa717x_write_regs(sd, reg_init_composite_input); } return 0; } -static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) +static int saa717x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - struct saa717x_state *decoder = i2c_get_clientdata(client); - - v4l_dbg(1, debug, client, "IOCTL: %08x\n", cmd); + int i; - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return saa717x_set_audio_clock_freq(client, *(u32 *)arg); + for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) + if (qc->id && qc->id == saa717x_qctrl[i].id) { + memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; +} - case VIDIOC_G_CTRL: - return saa717x_get_v4lctrl(client, (struct v4l2_control *)arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int saa717x_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_S_CTRL: - return saa717x_set_v4lctrl(client, (struct v4l2_control *)arg); + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = saa717x_read(sd, reg->reg); + return 0; +} - case VIDIOC_QUERYCTRL: { - struct v4l2_queryctrl *qc = arg; - int i; +static int saa717x_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr = reg->reg & 0xffff; + u8 val = reg->val & 0xff; - for (i = 0; i < ARRAY_SIZE(saa717x_qctrl); i++) - if (qc->id && qc->id == saa717x_qctrl[i].id) { - memcpy(qc, &saa717x_qctrl[i], sizeof(*qc)); - return 0; - } + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; - } - -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - reg->val = saa717x_read(client, reg->reg); - break; - } + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + saa717x_write(sd, addr, val); + return 0; +} +#endif - case VIDIOC_DBG_S_REGISTER: { - struct v4l2_register *reg = arg; - u16 addr = reg->reg & 0xffff; - u8 val = reg->val & 0xff; +static int saa717x_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct v4l2_pix_format *pix; + int prescale, h_scale, v_scale; - if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - saa717x_write(client, addr, val); - break; - } -#endif + pix = &fmt->fmt.pix; + v4l2_dbg(1, debug, sd, "decoder set size\n"); - case VIDIOC_S_FMT: { - struct v4l2_format *fmt = (struct v4l2_format *)arg; - struct v4l2_pix_format *pix; - int prescale, h_scale, v_scale; - - pix = &fmt->fmt.pix; - v4l_dbg(1, debug, client, "decoder set size\n"); - - /* FIXME need better bounds checking here */ - if (pix->width < 1 || pix->width > 1440) - return -EINVAL; - if (pix->height < 1 || pix->height > 960) - return -EINVAL; - - /* scaling setting */ - /* NTSC and interlace only */ - prescale = SAA717X_NTSC_WIDTH / pix->width; - if (prescale == 0) - prescale = 1; - h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width; - /* interlace */ - v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height; - - /* Horizontal prescaling etc */ - set_h_prescale(client, 0, prescale); - set_h_prescale(client, 1, prescale); - - /* Horizontal scaling increment */ - /* TASK A */ - saa717x_write(client, 0x6C, (u8)(h_scale & 0xFF)); - saa717x_write(client, 0x6D, (u8)((h_scale >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0xAC, (u8)(h_scale & 0xFF)); - saa717x_write(client, 0xAD, (u8)((h_scale >> 8) & 0xFF)); - - /* Vertical prescaling etc */ - set_v_scale(client, 0, v_scale); - set_v_scale(client, 1, v_scale); - - /* set video output size */ - /* video number of pixels at output */ - /* TASK A */ - saa717x_write(client, 0x5C, (u8)(pix->width & 0xFF)); - saa717x_write(client, 0x5D, (u8)((pix->width >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0x9C, (u8)(pix->width & 0xFF)); - saa717x_write(client, 0x9D, (u8)((pix->width >> 8) & 0xFF)); - - /* video number of lines at output */ - /* TASK A */ - saa717x_write(client, 0x5E, (u8)(pix->height & 0xFF)); - saa717x_write(client, 0x5F, (u8)((pix->height >> 8) & 0xFF)); - /* TASK B */ - saa717x_write(client, 0x9E, (u8)(pix->height & 0xFF)); - saa717x_write(client, 0x9F, (u8)((pix->height >> 8) & 0xFF)); - break; - } + /* FIXME need better bounds checking here */ + if (pix->width < 1 || pix->width > 1440) + return -EINVAL; + if (pix->height < 1 || pix->height > 960) + return -EINVAL; - case AUDC_SET_RADIO: - decoder->radio = 1; - break; + /* scaling setting */ + /* NTSC and interlace only */ + prescale = SAA717X_NTSC_WIDTH / pix->width; + if (prescale == 0) + prescale = 1; + h_scale = 1024 * SAA717X_NTSC_WIDTH / prescale / pix->width; + /* interlace */ + v_scale = 512 * 2 * SAA717X_NTSC_HEIGHT / pix->height; + + /* Horizontal prescaling etc */ + set_h_prescale(sd, 0, prescale); + set_h_prescale(sd, 1, prescale); + + /* Horizontal scaling increment */ + /* TASK A */ + saa717x_write(sd, 0x6C, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0x6D, (u8)((h_scale >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0xAC, (u8)(h_scale & 0xFF)); + saa717x_write(sd, 0xAD, (u8)((h_scale >> 8) & 0xFF)); + + /* Vertical prescaling etc */ + set_v_scale(sd, 0, v_scale); + set_v_scale(sd, 1, v_scale); + + /* set video output size */ + /* video number of pixels at output */ + /* TASK A */ + saa717x_write(sd, 0x5C, (u8)(pix->width & 0xFF)); + saa717x_write(sd, 0x5D, (u8)((pix->width >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9C, (u8)(pix->width & 0xFF)); + saa717x_write(sd, 0x9D, (u8)((pix->width >> 8) & 0xFF)); + + /* video number of lines at output */ + /* TASK A */ + saa717x_write(sd, 0x5E, (u8)(pix->height & 0xFF)); + saa717x_write(sd, 0x5F, (u8)((pix->height >> 8) & 0xFF)); + /* TASK B */ + saa717x_write(sd, 0x9E, (u8)(pix->height & 0xFF)); + saa717x_write(sd, 0x9F, (u8)((pix->height >> 8) & 0xFF)); + return 0; +} - case VIDIOC_S_STD: { - v4l2_std_id std = *(v4l2_std_id *) arg; +static int saa717x_s_radio(struct v4l2_subdev *sd) +{ + struct saa717x_state *decoder = to_state(sd); - v4l_dbg(1, debug, client, "decoder set norm "); - v4l_dbg(1, debug, client, "(not yet implementd)\n"); + decoder->radio = 1; + return 0; +} - decoder->radio = 0; - decoder->std = std; - break; - } +static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa717x_state *decoder = to_state(sd); - case VIDIOC_INT_G_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; + v4l2_dbg(1, debug, sd, "decoder set norm "); + v4l2_dbg(1, debug, sd, "(not yet implementd)\n"); - route->input = decoder->audio_input; - route->output = 0; - break; - } + decoder->radio = 0; + decoder->std = std; + return 0; +} - case VIDIOC_INT_S_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; +static int saa717x_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct saa717x_state *decoder = to_state(sd); - if (route->input < 3) { /* FIXME! --tadachi */ - decoder->audio_input = route->input; - v4l_dbg(1, debug, client, + if (route->input < 3) { /* FIXME! --tadachi */ + decoder->audio_input = route->input; + v4l2_dbg(1, debug, sd, "set decoder audio input to %d\n", decoder->audio_input); - set_audio_regs(client, decoder); - break; - } - return -ERANGE; - } - - case VIDIOC_INT_S_VIDEO_ROUTING: { - struct v4l2_routing *route = arg; - int inp = route->input; - - return saa717x_set_video_input(client, decoder, inp); + set_audio_regs(sd, decoder); + return 0; } + return -ERANGE; +} - case VIDIOC_STREAMON: { - v4l_dbg(1, debug, client, "decoder enable output\n"); - decoder->enable = 1; - saa717x_write(client, 0x193, 0xa6); - break; - } +static int saa717x_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct saa717x_state *decoder = to_state(sd); - case VIDIOC_STREAMOFF: { - v4l_dbg(1, debug, client, "decoder disable output\n"); - decoder->enable = 0; - saa717x_write(client, 0x193, 0x26); /* right? FIXME!--tadachi */ - break; - } + v4l2_dbg(1, debug, sd, "decoder %s output\n", + enable ? "enable" : "disable"); + decoder->enable = enable; + saa717x_write(sd, 0x193, enable ? 0xa6 : 0x26); + return 0; +} - /* change audio mode */ - case VIDIOC_S_TUNER: { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - int audio_mode; - char *mes[4] = { - "MONO", "STEREO", "LANG1", "LANG2/SAP" - }; +/* change audio mode */ +static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int audio_mode; + char *mes[4] = { + "MONO", "STEREO", "LANG1", "LANG2/SAP" + }; - audio_mode = V4L2_TUNER_MODE_STEREO; + audio_mode = V4L2_TUNER_MODE_STEREO; - switch (vt->audmode) { + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: audio_mode = TUNER_AUDIO_MONO; break; @@ -1353,70 +1332,101 @@ static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) case V4L2_TUNER_MODE_LANG1: audio_mode = TUNER_AUDIO_LANG1; break; - } - - v4l_dbg(1, debug, client, "change audio mode to %s\n", - mes[audio_mode]); - decoder->tuner_audio_mode = audio_mode; - /* The registers are not changed here. */ - /* See DECODER_ENABLE_OUTPUT section. */ - set_audio_mode(client, decoder->tuner_audio_mode); - break; } - case VIDIOC_G_TUNER: { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - int dual_f, stereo_f; + v4l2_dbg(1, debug, sd, "change audio mode to %s\n", + mes[audio_mode]); + decoder->tuner_audio_mode = audio_mode; + /* The registers are not changed here. */ + /* See DECODER_ENABLE_OUTPUT section. */ + set_audio_mode(sd, decoder->tuner_audio_mode); + return 0; +} - if (decoder->radio) - break; - get_inf_dev_status(client, &dual_f, &stereo_f); +static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct saa717x_state *decoder = to_state(sd); + int dual_f, stereo_f; - v4l_dbg(1, debug, client, "DETECT==st:%d dual:%d\n", - stereo_f, dual_f); + if (decoder->radio) + return 0; + get_inf_dev_status(sd, &dual_f, &stereo_f); - /* mono */ - if ((dual_f == 0) && (stereo_f == 0)) { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==MONO\n"); - } + v4l2_dbg(1, debug, sd, "DETECT==st:%d dual:%d\n", + stereo_f, dual_f); - /* stereo */ - if (stereo_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_STEREO || - vt->audmode == V4L2_TUNER_MODE_LANG1) { - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - v4l_dbg(1, debug, client, "DETECT==ST(ST)\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==ST(MONO)\n"); - } - } + /* mono */ + if ((dual_f == 0) && (stereo_f == 0)) { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==MONO\n"); + } - /* dual */ - if (dual_f == 1) { - if (vt->audmode == V4L2_TUNER_MODE_LANG2) { - vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==DUAL1\n"); - } else { - vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; - v4l_dbg(1, debug, client, "DETECT==DUAL2\n"); - } + /* stereo */ + if (stereo_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_STEREO || + vt->audmode == V4L2_TUNER_MODE_LANG1) { + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + v4l2_dbg(1, debug, sd, "DETECT==ST(ST)\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==ST(MONO)\n"); } - break; } - case VIDIOC_LOG_STATUS: - /* not yet implemented */ - break; - - default: - return -EINVAL; + /* dual */ + if (dual_f == 1) { + if (vt->audmode == V4L2_TUNER_MODE_LANG2) { + vt->rxsubchans = V4L2_TUNER_SUB_LANG2 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL1\n"); + } else { + vt->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_MONO; + v4l2_dbg(1, debug, sd, "DETECT==DUAL2\n"); + } } - return 0; } +static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa717x_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = saa717x_g_register, + .s_register = saa717x_s_register, +#endif + .queryctrl = saa717x_queryctrl, + .g_ctrl = saa717x_g_ctrl, + .s_ctrl = saa717x_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { + .g_tuner = saa717x_g_tuner, + .s_tuner = saa717x_s_tuner, + .s_std = saa717x_s_std, + .s_radio = saa717x_s_radio, +}; + +static const struct v4l2_subdev_video_ops saa717x_video_ops = { + .s_routing = saa717x_s_video_routing, + .s_fmt = saa717x_s_fmt, + .s_stream = saa717x_s_stream, +}; + +static const struct v4l2_subdev_audio_ops saa717x_audio_ops = { + .s_routing = saa717x_s_audio_routing, +}; + +static const struct v4l2_subdev_ops saa717x_ops = { + .core = &saa717x_core_ops, + .tuner = &saa717x_tuner_ops, + .audio = &saa717x_audio_ops, + .video = &saa717x_video_ops, +}; + /* ----------------------------------------------------------------------- */ @@ -1427,6 +1437,7 @@ static int saa717x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct saa717x_state *decoder; + struct v4l2_subdev *sd; u8 id = 0; char *p = ""; @@ -1434,16 +1445,24 @@ static int saa717x_probe(struct i2c_client *client, if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; + decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &saa717x_ops); + #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) snprintf(client->name, sizeof(client->name) - 1, "saa717x"); #endif - if (saa717x_write(client, 0x5a4, 0xfe) && - saa717x_write(client, 0x5a5, 0x0f) && - saa717x_write(client, 0x5a6, 0x00) && - saa717x_write(client, 0x5a7, 0x01)) - id = saa717x_read(client, 0x5a0); + if (saa717x_write(sd, 0x5a4, 0xfe) && + saa717x_write(sd, 0x5a5, 0x0f) && + saa717x_write(sd, 0x5a6, 0x00) && + saa717x_write(sd, 0x5a7, 0x01)) + id = saa717x_read(sd, 0x5a0); if (id != 0xc2 && id != 0x32 && id != 0xf2 && id != 0x6c) { - v4l_dbg(1, debug, client, "saa717x not found (id=%02x)\n", id); + v4l2_dbg(1, debug, sd, "saa717x not found (id=%02x)\n", id); + kfree(decoder); return -ENODEV; } if (id == 0xc2) @@ -1454,14 +1473,8 @@ static int saa717x_probe(struct i2c_client *client, p = "saa7174HL"; else p = "saa7171"; - v4l_info(client, "%s found @ 0x%x (%s)\n", p, + v4l2_info(sd, "%s found @ 0x%x (%s)\n", p, client->addr << 1, client->adapter->name); - - decoder = kzalloc(sizeof(struct saa717x_state), GFP_KERNEL); - i2c_set_clientdata(client, decoder); - - if (decoder == NULL) - return -ENOMEM; decoder->std = V4L2_STD_NTSC; decoder->input = -1; decoder->enable = 1; @@ -1490,15 +1503,15 @@ static int saa717x_probe(struct i2c_client *client, decoder->audio_main_volume = (decoder->audio_main_vol_r + 41) * 65535 / (24 - (-40)); - v4l_dbg(1, debug, client, "writing init values\n"); + v4l2_dbg(1, debug, sd, "writing init values\n"); /* FIXME!! */ - saa717x_write_regs(client, reg_init_initialize); - set_video_output_level_regs(client, decoder); + saa717x_write_regs(sd, reg_init_initialize); + set_video_output_level_regs(sd, decoder); /* set bass,treble to 0db 20041101 K.Ohta */ decoder->audio_main_bass = 0; decoder->audio_main_treble = 0; - set_audio_regs(client, decoder); + set_audio_regs(sd, decoder); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(2*HZ); @@ -1507,7 +1520,10 @@ static int saa717x_probe(struct i2c_client *client, static int saa717x_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 5dd4bd660..e649f2eb7 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -19,7 +19,7 @@ #include <linux/videodev.h> #include <media/tuner.h> #include <media/tuner-types.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-i2c-drv-legacy.h> #include "mt20xx.h" @@ -79,6 +79,7 @@ struct tuner { /* device */ struct dvb_frontend fe; struct i2c_client *i2c; + struct v4l2_subdev sd; struct list_head list; unsigned int using_v4l2:1; @@ -96,6 +97,11 @@ struct tuner { const char *name; }; +static inline struct tuner *to_tuner(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tuner, sd); +} + /* standard i2c insmod options */ static unsigned short normal_i2c[] = { #if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) @@ -214,7 +220,7 @@ static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg) static void tuner_status(struct dvb_frontend *fe); -static struct analog_demod_ops tuner_core_ops = { +static struct analog_demod_ops tuner_analog_ops = { .set_params = fe_set_params, .standby = fe_standby, .has_signal = fe_has_signal, @@ -225,7 +231,7 @@ static struct analog_demod_ops tuner_core_ops = { /* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */ static void set_tv_freq(struct i2c_client *c, unsigned int freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { @@ -260,7 +266,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) static void set_radio_freq(struct i2c_client *c, unsigned int freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { @@ -295,7 +301,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) static void set_freq(struct i2c_client *c, unsigned long freq) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); switch (t->mode) { case V4L2_TUNER_RADIO: @@ -348,7 +354,7 @@ static void set_type(struct i2c_client *c, unsigned int type, unsigned int new_mode_mask, unsigned int new_config, int (*tuner_callback) (void *dev, int component, int cmd, int arg)) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; unsigned char buffer[4]; @@ -471,7 +477,7 @@ static void set_type(struct i2c_client *c, unsigned int type, t->name = fe_tuner_ops->info.name; t->fe.analog_demod_priv = t; - memcpy(analog_ops, &tuner_core_ops, + memcpy(analog_ops, &tuner_analog_ops, sizeof(struct analog_demod_ops)); } else { @@ -516,7 +522,7 @@ attach_failed: static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && (t->mode_mask & tun_setup->mode_mask))) || @@ -758,43 +764,56 @@ static inline int check_v4l2(struct tuner *t) return 0; } -static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) +static int tuner_s_type_addr(struct v4l2_subdev *sd, struct tuner_setup *type) { - struct tuner *t = i2c_get_clientdata(client); - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + tuner_dbg("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", + type->type, + type->addr, + type->mode_mask, + type->config); + + set_addr(client, type); + return 0; +} + +static int tuner_s_radio(struct v4l2_subdev *sd) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") + == -EINVAL) + return 0; + if (t->radio_freq) + set_freq(client, t->radio_freq); + return 0; +} + +static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) +{ + struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (tuner_debug > 1) { - v4l_i2c_print_ioctl(client,cmd); - printk("\n"); - } + if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) + return 0; + t->mode = T_STANDBY; + if (analog_ops->standby) + analog_ops->standby(&t->fe); + return 0; +} - switch (cmd) { - /* --- configuration --- */ - case TUNER_SET_TYPE_ADDR: - tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", - ((struct tuner_setup *)arg)->type, - ((struct tuner_setup *)arg)->addr, - ((struct tuner_setup *)arg)->mode_mask, - ((struct tuner_setup *)arg)->config); - - set_addr(client, (struct tuner_setup *)arg); - break; - case AUDC_SET_RADIO: - if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") - == -EINVAL) - return 0; - if (t->radio_freq) - set_freq(client, t->radio_freq); - break; - case TUNER_SET_STANDBY: - if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) - return 0; - t->mode = T_STANDBY; - if (analog_ops->standby) - analog_ops->standby(&t->fe); - break; #ifdef CONFIG_VIDEO_ALLOW_V4L1 +static int tuner_ioctl(struct v4l2_subdev *sd, int cmd, void *arg) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + + switch (cmd) { case VIDIOCSAUDIO: if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL) return 0; @@ -907,149 +926,172 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } return 0; } + } + return -ENOIOCTLCMD; +} #endif - case TUNER_SET_CONFIG: - { - struct v4l2_priv_tun_config *cfg = arg; - if (t->type != cfg->tuner) - break; +static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - if (analog_ops->set_config) { - analog_ops->set_config(&t->fe, cfg->priv); - break; - } + if (t->type != cfg->tuner) + return 0; - tuner_dbg("Tuner frontend module has no way to set config\n"); - break; + if (analog_ops->set_config) { + analog_ops->set_config(&t->fe, cfg->priv); + return 0; } - /* --- v4l ioctls --- */ - /* take care: bttv does userspace copying, we'll get a - kernel pointer here... */ - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - if (set_mode (client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") - == -EINVAL) - return 0; + tuner_dbg("Tuner frontend module has no way to set config\n"); + return 0; +} - switch_v4l2(); +/* --- v4l ioctls --- */ +/* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ +static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - t->std = *id; - tuner_fixup_std(t); - if (t->tv_freq) - set_freq(client, t->tv_freq); - break; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") + == -EINVAL) + return 0; - if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY") - == -EINVAL) - return 0; - switch_v4l2(); - set_freq(client,f->frequency); + switch_v4l2(); - break; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + t->std = std; + tuner_fixup_std(t); + if (t->tv_freq) + set_freq(client, t->tv_freq); + return 0; +} - if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) - return 0; - switch_v4l2(); - f->type = t->mode; - if (fe_tuner_ops->get_frequency) { - u32 abs_freq; - - fe_tuner_ops->get_frequency(&t->fe, &abs_freq); - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - (abs_freq * 2 + 125/2) / 125 : - (abs_freq + 62500/2) / 62500; - break; - } - f->frequency = (V4L2_TUNER_RADIO == t->mode) ? - t->radio_freq : t->tv_freq; - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *tuner = arg; +static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) - return 0; - switch_v4l2(); - - tuner->type = t->mode; - if (analog_ops->get_afc) - tuner->afc = analog_ops->get_afc(&t->fe); - if (t->mode == V4L2_TUNER_ANALOG_TV) - tuner->capability |= V4L2_TUNER_CAP_NORM; - if (t->mode != V4L2_TUNER_RADIO) { - tuner->rangelow = tv_range[0] * 16; - tuner->rangehigh = tv_range[1] * 16; - break; - } + if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY") + == -EINVAL) + return 0; + switch_v4l2(); + set_freq(client, f->frequency); - /* radio mode */ - tuner->rxsubchans = - V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - tuner->rxsubchans = - (tuner_status & TUNER_STATUS_STEREO) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } else { - if (analog_ops->is_stereo) { - tuner->rxsubchans = - analog_ops->is_stereo(&t->fe) ? - V4L2_TUNER_SUB_STEREO : - V4L2_TUNER_SUB_MONO; - } - } - if (analog_ops->has_signal) - tuner->signal = analog_ops->has_signal(&t->fe); - tuner->capability |= - V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; - tuner->audmode = t->audmode; - tuner->rangelow = radio_range[0] * 16000; - tuner->rangehigh = radio_range[1] * 16000; - break; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *tuner = arg; + return 0; +} - if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) - return 0; +static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tuner *t = to_tuner(sd); + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - switch_v4l2(); + if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) + return 0; + switch_v4l2(); + f->type = t->mode; + if (fe_tuner_ops->get_frequency) { + u32 abs_freq; + + fe_tuner_ops->get_frequency(&t->fe, &abs_freq); + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + (abs_freq * 2 + 125/2) / 125 : + (abs_freq + 62500/2) / 62500; + return 0; + } + f->frequency = (V4L2_TUNER_RADIO == t->mode) ? + t->radio_freq : t->tv_freq; + return 0; +} - /* do nothing unless we're a radio tuner */ - if (t->mode != V4L2_TUNER_RADIO) - break; - t->audmode = tuner->audmode; - set_radio_freq(client, t->radio_freq); - break; +static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; + + if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) + return 0; + switch_v4l2(); + + vt->type = t->mode; + if (analog_ops->get_afc) + vt->afc = analog_ops->get_afc(&t->fe); + if (t->mode == V4L2_TUNER_ANALOG_TV) + vt->capability |= V4L2_TUNER_CAP_NORM; + if (t->mode != V4L2_TUNER_RADIO) { + vt->rangelow = tv_range[0] * 16; + vt->rangehigh = tv_range[1] * 16; + return 0; + } + + /* radio mode */ + vt->rxsubchans = + V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + if (fe_tuner_ops->get_status) { + u32 tuner_status; + + fe_tuner_ops->get_status(&t->fe, &tuner_status); + vt->rxsubchans = + (tuner_status & TUNER_STATUS_STEREO) ? + V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; + } else { + if (analog_ops->is_stereo) { + vt->rxsubchans = + analog_ops->is_stereo(&t->fe) ? + V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; } - case VIDIOC_LOG_STATUS: - if (analog_ops->tuner_status) - analog_ops->tuner_status(&t->fe); - break; } + if (analog_ops->has_signal) + vt->signal = analog_ops->has_signal(&t->fe); + vt->capability |= + V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + vt->audmode = t->audmode; + vt->rangelow = radio_range[0] * 16000; + vt->rangehigh = radio_range[1] * 16000; + return 0; +} + +static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct tuner *t = to_tuner(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) + return 0; + + switch_v4l2(); + + /* do nothing unless we're a radio tuner */ + if (t->mode != V4L2_TUNER_RADIO) + return 0; + t->audmode = vt->audmode; + set_radio_freq(client, t->radio_freq); + return 0; +} + +static int tuner_log_status(struct v4l2_subdev *sd) +{ + struct tuner *t = to_tuner(sd); + struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + if (analog_ops->tuner_status) + analog_ops->tuner_status(&t->fe); return 0; } +static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + static int tuner_suspend(struct i2c_client *c, pm_message_t state) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("suspend\n"); /* FIXME: power down ??? */ @@ -1058,7 +1100,7 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state) static int tuner_resume(struct i2c_client *c) { - struct tuner *t = i2c_get_clientdata(c); + struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); if (V4L2_TUNER_RADIO == t->mode) { @@ -1071,6 +1113,30 @@ static int tuner_resume(struct i2c_client *c) return 0; } +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops tuner_core_ops = { + .log_status = tuner_log_status, + .s_standby = tuner_s_standby, + .ioctl = tuner_ioctl, +}; + +static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { + .s_std = tuner_s_std, + .s_radio = tuner_s_radio, + .g_tuner = tuner_g_tuner, + .s_tuner = tuner_s_tuner, + .s_frequency = tuner_s_frequency, + .g_frequency = tuner_g_frequency, + .s_type_addr = tuner_s_type_addr, + .s_config = tuner_s_config, +}; + +static const struct v4l2_subdev_ops tuner_ops = { + .core = &tuner_core_ops, + .tuner = &tuner_tuner_ops, +}; + /* ---------------------------------------------------------------------- */ static LIST_HEAD(tuner_list); @@ -1119,9 +1185,9 @@ static int tuner_probe(struct i2c_client *client, t = kzalloc(sizeof(struct tuner), GFP_KERNEL); if (NULL == t) return -ENOMEM; + v4l2_i2c_subdev_init(&t->sd, client, &tuner_ops); t->i2c = client; t->name = "(tuner unset)"; - i2c_set_clientdata(client, t); t->type = UNSET; t->audmode = V4L2_TUNER_MODE_STEREO; t->mode_mask = T_UNINITIALIZED; @@ -1271,8 +1337,9 @@ static int tuner_legacy_probe(struct i2c_adapter *adap) static int tuner_remove(struct i2c_client *client) { - struct tuner *t = i2c_get_clientdata(client); + struct tuner *t = to_tuner(i2c_get_clientdata(client)); + v4l2_device_unregister_subdev(&t->sd); tuner_detach(&t->fe); t->fe.analog_demod_priv = NULL; diff --git a/linux/drivers/media/video/upd64031a.c b/linux/drivers/media/video/upd64031a.c index dfca5ed6e..df5f4b0c8 100644 --- a/linux/drivers/media/video/upd64031a.c +++ b/linux/drivers/media/video/upd64031a.c @@ -26,7 +26,7 @@ #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/upd64031a.h> @@ -68,6 +68,7 @@ enum { }; struct upd64031a_state { + struct v4l2_subdev sd; u8 regs[TOT_REGS]; u8 gr_mode; u8 direct_3dycs_connect; @@ -75,6 +76,11 @@ struct upd64031a_state { u8 ext_vert_sync; }; +static inline struct upd64031a_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64031a_state, sd); +} + static u8 upd64031a_init[] = { 0x00, 0xb8, 0x48, 0xd2, 0xe6, 0x03, 0x10, 0x0b, 0xaf, 0x7f, @@ -84,8 +90,9 @@ static u8 upd64031a_init[] = { /* ------------------------------------------------------------------------ */ -static u8 upd64031a_read(struct i2c_client *client, u8 reg) +static u8 upd64031a_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; if (reg >= sizeof(buf)) @@ -96,106 +103,127 @@ static u8 upd64031a_read(struct i2c_client *client, u8 reg) /* ------------------------------------------------------------------------ */ -static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val) +static void upd64031a_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; buf[0] = reg; buf[1] = val; - v4l_dbg(1, debug, client, "write reg: %02X val: %02X\n", reg, val); + v4l2_dbg(1, debug, sd, "write reg: %02X val: %02X\n", reg, val); if (i2c_master_send(client, buf, 2) != 2) - v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); } /* ------------------------------------------------------------------------ */ /* The input changed due to new input or channel changed */ -static void upd64031a_change(struct i2c_client *client) +static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) { - struct upd64031a_state *state = i2c_get_clientdata(client); + struct upd64031a_state *state = to_state(sd); u8 reg = state->regs[R00]; - v4l_dbg(1, debug, client, "changed input or channel\n"); - upd64031a_write(client, R00, reg | 0x10); - upd64031a_write(client, R00, reg & ~0x10); + v4l2_dbg(1, debug, sd, "changed input or channel\n"); + upd64031a_write(sd, R00, reg | 0x10); + upd64031a_write(sd, R00, reg & ~0x10); + return 0; } /* ------------------------------------------------------------------------ */ +static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct upd64031a_state *state = to_state(sd); + u8 r00, r05, r08; + + state->gr_mode = (route->input & 3) << 6; + state->direct_3dycs_connect = (route->input & 0xc) << 4; + state->ext_comp_sync = + (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + state->ext_vert_sync = + (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; + r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; + r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | + state->ext_comp_sync | state->ext_vert_sync; + r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | + state->direct_3dycs_connect; + upd64031a_write(sd, R00, r00); + upd64031a_write(sd, R05, r05); + upd64031a_write(sd, R08, r08); + return upd64031a_s_frequency(sd, NULL); +} + +static int upd64031a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64031A, 0); +} + +static int upd64031a_log_status(struct v4l2_subdev *sd) +{ + v4l2_info(sd, "Status: SA00=0x%02x SA01=0x%02x\n", + upd64031a_read(sd, 0), upd64031a_read(sd, 1)); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int upd64031a_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64031a_read(sd, reg->reg & 0xff); + return 0; +} + +static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64031a_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} +#endif + static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg) { - struct upd64031a_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_S_FREQUENCY: - upd64031a_change(client); - break; - - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = (state->gr_mode >> 6) | - (state->direct_3dycs_connect >> 4) | - (state->ext_comp_sync >> 1) | - (state->ext_vert_sync >> 2); - route->output = 0; - break; - - case VIDIOC_INT_S_VIDEO_ROUTING: - { - u8 r00, r05, r08; - - state->gr_mode = (route->input & 3) << 6; - state->direct_3dycs_connect = (route->input & 0xc) << 4; - state->ext_comp_sync = - (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; - state->ext_vert_sync = - (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; - r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; - r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | - state->ext_comp_sync | state->ext_vert_sync; - r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | - state->direct_3dycs_connect; - upd64031a_write(client, R00, r00); - upd64031a_write(client, R05, r05); - upd64031a_write(client, R08, r08); - upd64031a_change(client); - break; - } - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n", - upd64031a_read(client, 0), upd64031a_read(client, 1)); - break; + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ +static const struct v4l2_subdev_core_ops upd64031a_core_ops = { + .log_status = upd64031a_log_status, + .g_chip_ident = upd64031a_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (!v4l2_chip_match_i2c_client(client, - reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) { - reg->val = upd64031a_read(client, reg->reg & 0xff); - break; - } - upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } + .g_register = upd64031a_g_register, + .s_register = upd64031a_s_register, #endif +}; - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_UPD64031A, 0); +static const struct v4l2_subdev_tuner_ops upd64031a_tuner_ops = { + .s_frequency = upd64031a_s_frequency, +}; - default: - break; - } - return 0; -} +static const struct v4l2_subdev_video_ops upd64031a_video_ops = { + .s_routing = upd64031a_s_routing, +}; + +static const struct v4l2_subdev_ops upd64031a_ops = { + .core = &upd64031a_core_ops, + .tuner = &upd64031a_tuner_ops, + .video = &upd64031a_video_ops, +}; /* ------------------------------------------------------------------------ */ @@ -205,6 +233,7 @@ static int upd64031a_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct upd64031a_state *state; + struct v4l2_subdev *sd; int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -216,19 +245,23 @@ static int upd64031a_probe(struct i2c_client *client, state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64031a_ops); memcpy(state->regs, upd64031a_init, sizeof(state->regs)); state->gr_mode = UPD64031A_GR_ON << 6; state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; state->ext_comp_sync = state->ext_vert_sync = 0; for (i = 0; i < TOT_REGS; i++) - upd64031a_write(client, i, state->regs[i]); + upd64031a_write(sd, i, state->regs[i]); return 0; } static int upd64031a_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/upd64083.c b/linux/drivers/media/video/upd64083.c index 5f0c6919d..fe71b038b 100644 --- a/linux/drivers/media/video/upd64083.c +++ b/linux/drivers/media/video/upd64083.c @@ -26,7 +26,7 @@ #include <linux/kernel.h> #include <linux/i2c.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include <media/upd64083.h> @@ -57,11 +57,17 @@ enum { }; struct upd64083_state { + struct v4l2_subdev sd; u8 mode; u8 ext_y_adc; u8 regs[TOT_REGS]; }; +static inline struct upd64083_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct upd64083_state, sd); +} + /* Initial values when used in combination with the NEC upd64031a ghost reduction chip. */ static u8 upd64083_init[] = { @@ -74,34 +80,24 @@ static u8 upd64083_init[] = { /* ------------------------------------------------------------------------ */ -static void upd64083_log_status(struct i2c_client *client) -{ - u8 buf[7]; - - i2c_master_recv(client, buf, 7); - v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " - "SA04=%02x SA05=%02x SA06=%02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); -} - -/* ------------------------------------------------------------------------ */ - -static void upd64083_write(struct i2c_client *client, u8 reg, u8 val) +static void upd64083_write(struct v4l2_subdev *sd, u8 reg, u8 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[2]; buf[0] = reg; buf[1] = val; - v4l_dbg(1, debug, client, "write reg: %02x val: %02x\n", reg, val); + v4l2_dbg(1, debug, sd, "write reg: %02x val: %02x\n", reg, val); if (i2c_master_send(client, buf, 2) != 2) - v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); + v4l2_err(sd, "I/O error write 0x%02x/0x%02x\n", reg, val); } /* ------------------------------------------------------------------------ */ #ifdef CONFIG_VIDEO_ADV_DEBUG -static u8 upd64083_read(struct i2c_client *client, u8 reg) +static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 buf[7]; if (reg >= sizeof(buf)) @@ -113,67 +109,94 @@ static u8 upd64083_read(struct i2c_client *client, u8 reg) /* ------------------------------------------------------------------------ */ -static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) +static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct upd64083_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - - switch (cmd) { - case VIDIOC_INT_G_VIDEO_ROUTING: - route->input = (state->mode >> 6) | (state->ext_y_adc >> 3); - route->output = 0; - break; - - case VIDIOC_INT_S_VIDEO_ROUTING: - { - u8 r00, r02; - - if (route->input > 7 || (route->input & 6) == 6) - return -EINVAL; - state->mode = (route->input & 3) << 6; - state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; - r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; - r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; - upd64083_write(client, R00, r00); - upd64083_write(client, R02, r02); - break; - } - - case VIDIOC_LOG_STATUS: - upd64083_log_status(client); - break; + struct upd64083_state *state = to_state(sd); + u8 r00, r02; + + if (route->input > 7 || (route->input & 6) == 6) + return -EINVAL; + state->mode = (route->input & 3) << 6; + state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; + r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; + r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; + upd64083_write(sd, R00, r00); + upd64083_write(sd, R02, r02); + return 0; +} #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; +static int upd64083_g_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, + reg->match_type, reg->match_chip)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->val = upd64083_read(sd, reg->reg & 0xff); + return 0; +} - if (!v4l2_chip_match_i2c_client(client, +static int upd64083_s_register(struct v4l2_subdev *sd, struct v4l2_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (cmd == VIDIOC_DBG_G_REGISTER) { - reg->val = upd64083_read(client, reg->reg & 0xff); - break; - } - upd64083_write(client, reg->reg & 0xff, reg->val & 0xff); - break; - } + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64083_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; +} #endif - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_UPD64083, 0); +static int upd64083_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_UPD64083, 0); +} - default: - break; - } +static int upd64083_log_status(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[7]; + i2c_master_recv(client, buf, 7); + v4l2_info(sd, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " + "SA04=%02x SA05=%02x SA06=%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); return 0; } +static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops upd64083_core_ops = { + .log_status = upd64083_log_status, + .g_chip_ident = upd64083_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = upd64083_g_register, + .s_register = upd64083_s_register, +#endif +}; + +static const struct v4l2_subdev_video_ops upd64083_video_ops = { + .s_routing = upd64083_s_routing, +}; + +static const struct v4l2_subdev_ops upd64083_ops = { + .core = &upd64083_core_ops, + .video = &upd64083_video_ops, +}; + /* ------------------------------------------------------------------------ */ /* i2c implementation */ @@ -182,6 +205,7 @@ static int upd64083_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct upd64083_state *state; + struct v4l2_subdev *sd; int i; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -193,19 +217,23 @@ static int upd64083_probe(struct i2c_client *client, state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; - i2c_set_clientdata(client, state); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &upd64083_ops); /* Initially assume that a ghost reduction chip is present */ state->mode = 0; /* YCS mode */ state->ext_y_adc = (1 << 5); memcpy(state->regs, upd64083_init, TOT_REGS); for (i = 0; i < TOT_REGS; i++) - upd64083_write(client, i, state->regs[i]); + upd64083_write(sd, i, state->regs[i]); return 0; } static int upd64083_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index e87a04a60..6d5e41ecc 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -58,6 +58,7 @@ #include <asm/div64.h> #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <linux/videodev2.h> @@ -802,4 +803,173 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver return err != -ENOMEM ? 0 : err; } EXPORT_SYMBOL(v4l2_i2c_attach); + +void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, + const struct v4l2_subdev_ops *ops) +{ + v4l2_subdev_init(sd, ops); + /* the owner is the same as the i2c_client's driver owner */ + sd->owner = client->driver->driver.owner; + /* i2c_client and v4l2_subdev point to one another */ + v4l2_set_subdevdata(sd, client); + i2c_set_clientdata(client, sd); + /* initialize name */ + snprintf(sd->name, sizeof(sd->name), "%s %d-%04x", + client->driver->driver.name, i2c_adapter_id(client->adapter), + client->addr); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +/* Supporting function to find a client on a specific address on the + given adapter. Used for legacy i2c drivers. */ +static struct i2c_client *v4l2_i2c_legacy_find_client(struct i2c_adapter *adap, u8 addr) +{ + struct i2c_client *result = NULL; + struct i2c_client *client; + struct list_head *item; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + down(&adap->clist_lock); +#else + mutex_lock(&adap->clist_lock); +#endif + list_for_each(item, &adap->clients) { + client = list_entry(item, struct i2c_client, list); + if (client->addr == addr) { + result = client; + break; + } + } +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + up(&adap->clist_lock); +#else + mutex_unlock(&adap->clist_lock); +#endif + return result; +} +#endif + + +/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + struct i2c_board_info info; +#endif + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + strlcpy(info.driver_name, client_type, sizeof(info.driver_name)); +#else + strlcpy(info.type, client_type, sizeof(info.type)); +#endif + info.addr = addr; + + /* Create the i2c client */ + client = i2c_new_device(adapter, &info); +#else + /* Legacy code: loading the module automatically + probes and creates the i2c_client on the adapter. + Try to find the client by walking the adapter's client list. */ + client = v4l2_i2c_legacy_find_client(adapter, addr); +#endif + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; + +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); + +/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) + returns the v4l2_device and that i2c_get_clientdata(client) + returns the v4l2_subdev. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + const unsigned short *addrs) +{ + struct v4l2_device *dev = i2c_get_adapdata(adapter); + struct v4l2_subdev *sd = NULL; + struct i2c_client *client = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + struct i2c_board_info info; +#endif + + BUG_ON(!dev); +#ifdef MODULE + if (module_name) + request_module(module_name); +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + strlcpy(info.driver_name, client_type, sizeof(info.driver_name)); +#else + strlcpy(info.type, client_type, sizeof(info.type)); +#endif + + /* Probe and create the i2c client */ + client = i2c_new_probed_device(adapter, &info, addrs); +#else + /* Legacy code: loading the module should automatically + probe and create the i2c_client on the adapter. + Try to find the client by walking the adapter's client list + for each of the possible addresses. */ + while (!client && *addrs != I2C_CLIENT_END) + client = v4l2_i2c_legacy_find_client(adapter, *addrs++); +#endif + /* Note: it is possible in the future that + c->driver is NULL if the driver is still being loaded. + We need better support from the kernel so that we + can easily wait for the load to finish. */ + if (client == NULL || client->driver == NULL) + return NULL; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + return NULL; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); + #endif diff --git a/linux/drivers/media/video/v4l2-device.c b/linux/drivers/media/video/v4l2-device.c new file mode 100644 index 000000000..9eefde031 --- /dev/null +++ b/linux/drivers/media/video/v4l2-device.c @@ -0,0 +1,86 @@ +/* + V4L2 device support. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> + +int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) +{ + if (dev == NULL || v4l2_dev == NULL) + return -EINVAL; + /* Warn if we apparently re-register a device */ + WARN_ON(dev_get_drvdata(dev)); + INIT_LIST_HEAD(&v4l2_dev->subdevs); + spin_lock_init(&v4l2_dev->lock); + v4l2_dev->dev = dev; + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s", + dev->driver->name, dev->bus_id); + dev_set_drvdata(dev, v4l2_dev); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register); + +void v4l2_device_unregister(struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd, *next; + + if (v4l2_dev == NULL || v4l2_dev->dev == NULL) + return; + dev_set_drvdata(v4l2_dev->dev, NULL); + /* unregister subdevs */ + list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) + v4l2_device_unregister_subdev(sd); + + v4l2_dev->dev = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister); + +int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd) +{ + /* Check for valid input */ + if (dev == NULL || sd == NULL || !sd->name[0]) + return -EINVAL; + /* Warn if we apparently re-register a subdev */ + WARN_ON(sd->dev); + if (!try_module_get(sd->owner)) + return -ENODEV; + sd->dev = dev; + spin_lock(&dev->lock); + list_add_tail(&sd->list, &dev->subdevs); + spin_unlock(&dev->lock); + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); + +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) +{ + /* return if it isn't registered */ + if (sd == NULL || sd->dev == NULL) + return; + spin_lock(&sd->dev->lock); + list_del(&sd->list); + spin_unlock(&sd->dev->lock); + sd->dev = NULL; + module_put(sd->owner); +} +EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/linux/drivers/media/video/v4l2-subdev.c b/linux/drivers/media/video/v4l2-subdev.c new file mode 100644 index 000000000..fe1f01c97 --- /dev/null +++ b/linux/drivers/media/video/v4l2-subdev.c @@ -0,0 +1,108 @@ +/* + V4L2 sub-device support. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-subdev.h> + +int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_QUERYCTRL: + return v4l2_subdev_call(sd, core, querymenu, arg); + case VIDIOC_G_CTRL: + return v4l2_subdev_call(sd, core, g_ctrl, arg); + case VIDIOC_S_CTRL: + return v4l2_subdev_call(sd, core, s_ctrl, arg); + case VIDIOC_QUERYMENU: + return v4l2_subdev_call(sd, core, queryctrl, arg); + case VIDIOC_LOG_STATUS: + return v4l2_subdev_call(sd, core, log_status); + case VIDIOC_G_CHIP_IDENT: + return v4l2_subdev_call(sd, core, g_chip_ident, arg); + case VIDIOC_INT_S_STANDBY: + return v4l2_subdev_call(sd, core, s_standby, *(u32 *)arg); + case VIDIOC_INT_RESET: + return v4l2_subdev_call(sd, core, reset, *(u32 *)arg); + case VIDIOC_INT_S_GPIO: + return v4l2_subdev_call(sd, core, s_gpio, *(u32 *)arg); + case VIDIOC_INT_INIT: + return v4l2_subdev_call(sd, core, init, *(u32 *)arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + return v4l2_subdev_call(sd, core, g_register, arg); + case VIDIOC_DBG_S_REGISTER: + return v4l2_subdev_call(sd, core, s_register, arg); +#endif + + case VIDIOC_INT_S_TUNER_MODE: + return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg); + case AUDC_SET_RADIO: + return v4l2_subdev_call(sd, tuner, s_radio); + case VIDIOC_S_TUNER: + return v4l2_subdev_call(sd, tuner, s_tuner, arg); + case VIDIOC_G_TUNER: + return v4l2_subdev_call(sd, tuner, g_tuner, arg); + case VIDIOC_S_STD: + return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg); + case VIDIOC_S_FREQUENCY: + return v4l2_subdev_call(sd, tuner, s_frequency, arg); + case VIDIOC_G_FREQUENCY: + return v4l2_subdev_call(sd, tuner, g_frequency, arg); + case TUNER_SET_TYPE_ADDR: + return v4l2_subdev_call(sd, tuner, s_type_addr, arg); + case TUNER_SET_CONFIG: + return v4l2_subdev_call(sd, tuner, s_config, arg); + + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg); + case VIDIOC_INT_S_AUDIO_ROUTING: + return v4l2_subdev_call(sd, audio, s_routing, arg); + case VIDIOC_INT_I2S_CLOCK_FREQ: + return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg); + + case VIDIOC_INT_S_VIDEO_ROUTING: + return v4l2_subdev_call(sd, video, s_routing, arg); + case VIDIOC_INT_S_CRYSTAL_FREQ: + return v4l2_subdev_call(sd, video, s_crystal_freq, arg); + case VIDIOC_INT_DECODE_VBI_LINE: + return v4l2_subdev_call(sd, video, decode_vbi_line, arg); + case VIDIOC_INT_S_VBI_DATA: + return v4l2_subdev_call(sd, video, s_vbi_data, arg); + case VIDIOC_INT_G_VBI_DATA: + return v4l2_subdev_call(sd, video, g_vbi_data, arg); + case VIDIOC_S_FMT: + return v4l2_subdev_call(sd, video, s_fmt, arg); + case VIDIOC_G_FMT: + return v4l2_subdev_call(sd, video, g_fmt, arg); + case VIDIOC_INT_S_STD_OUTPUT: + return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg); + case VIDIOC_STREAMON: + return v4l2_subdev_call(sd, video, s_stream, 1); + case VIDIOC_STREAMOFF: + return v4l2_subdev_call(sd, video, s_stream, 0); + + default: + return v4l2_subdev_call(sd, core, ioctl, cmd, arg); + } +} +EXPORT_SYMBOL_GPL(v4l2_subdev_command); diff --git a/linux/drivers/media/video/vp27smpx.c b/linux/drivers/media/video/vp27smpx.c index 80de9e796..cd9df6a1c 100644 --- a/linux/drivers/media/video/vp27smpx.c +++ b/linux/drivers/media/video/vp27smpx.c @@ -28,7 +28,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include "compat.h" @@ -46,13 +46,20 @@ I2C_CLIENT_INSMOD; /* ----------------------------------------------------------------------- */ struct vp27smpx_state { + struct v4l2_subdev sd; int radio; u32 audmode; }; -static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode) +static inline struct vp27smpx_state *to_state(struct v4l2_subdev *sd) { - struct vp27smpx_state *state = i2c_get_clientdata(client); + return container_of(sd, struct vp27smpx_state, sd); +} + +static void vp27smpx_set_audmode(struct v4l2_subdev *sd, u32 audmode) +{ + struct vp27smpx_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); u8 data[3] = { 0x00, 0x00, 0x04 }; switch (audmode) { @@ -69,55 +76,89 @@ static void vp27smpx_set_audmode(struct i2c_client *client, u32 audmode) } if (i2c_master_send(client, data, sizeof(data)) != sizeof(data)) - v4l_err(client, "%s: I/O error setting audmode\n", - client->name); + v4l2_err(sd, "I/O error setting audmode\n"); else state->audmode = audmode; } -static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) +static int vp27smpx_s_radio(struct v4l2_subdev *sd) { - struct vp27smpx_state *state = i2c_get_clientdata(client); - struct v4l2_tuner *vt = arg; + struct vp27smpx_state *state = to_state(sd); - switch (cmd) { - case AUDC_SET_RADIO: - state->radio = 1; - break; + state->radio = 1; + return 0; +} - case VIDIOC_S_STD: - state->radio = 0; - break; +static int vp27smpx_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct vp27smpx_state *state = to_state(sd); - case VIDIOC_S_TUNER: - if (!state->radio) - vp27smpx_set_audmode(client, vt->audmode); - break; + state->radio = 0; + return 0; +} - case VIDIOC_G_TUNER: - if (state->radio) - break; - vt->audmode = state->audmode; - vt->capability = V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - break; +static int vp27smpx_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, - V4L2_IDENT_VP27SMPX, 0); + if (!state->radio) + vp27smpx_set_audmode(sd, vt->audmode); + return 0; +} - case VIDIOC_LOG_STATUS: - v4l_info(client, "Audio Mode: %u%s\n", state->audmode, - state->radio ? " (Radio)" : ""); - break; +static int vp27smpx_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) +{ + struct vp27smpx_state *state = to_state(sd); + + if (state->radio) + return 0; + vt->audmode = state->audmode; + vt->capability = V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; +} - default: - return -EINVAL; - } +static int vp27smpx_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VP27SMPX, 0); +} + +static int vp27smpx_log_status(struct v4l2_subdev *sd) +{ + struct vp27smpx_state *state = to_state(sd); + + v4l2_info(sd, "Audio Mode: %u%s\n", state->audmode, + state->radio ? " (Radio)" : ""); return 0; } +static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { + .log_status = vp27smpx_log_status, + .g_chip_ident = vp27smpx_g_chip_ident, +}; + +static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { + .s_radio = vp27smpx_s_radio, + .s_std = vp27smpx_s_std, + .s_tuner = vp27smpx_s_tuner, + .g_tuner = vp27smpx_g_tuner, +}; + +static const struct v4l2_subdev_ops vp27smpx_ops = { + .core = &vp27smpx_core_ops, + .tuner = &vp27smpx_tuner_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -131,6 +172,7 @@ static int vp27smpx_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vp27smpx_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -145,17 +187,21 @@ static int vp27smpx_probe(struct i2c_client *client, state = kzalloc(sizeof(struct vp27smpx_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &vp27smpx_ops); state->audmode = V4L2_TUNER_MODE_STEREO; - i2c_set_clientdata(client, state); /* initialize vp27smpx */ - vp27smpx_set_audmode(client, state->audmode); + vp27smpx_set_audmode(sd, state->audmode); return 0; } static int vp27smpx_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c index f9d13b89a..ea311672c 100644 --- a/linux/drivers/media/video/wm8739.c +++ b/linux/drivers/media/video/wm8739.c @@ -28,7 +28,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> #include "compat.h" @@ -58,6 +58,7 @@ enum { }; struct wm8739_state { + struct v4l2_subdev sd; u32 clock_freq; u8 muted; u16 volume; @@ -66,43 +67,49 @@ struct wm8739_state { u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ }; +static inline struct wm8739_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct wm8739_state, sd); +} + /* ------------------------------------------------------------------------ */ -static int wm8739_write(struct i2c_client *client, int reg, u16 val) +static int wm8739_write(struct v4l2_subdev *sd, int reg, u16 val) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if (reg < 0 || reg >= TOT_REGS) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } - v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val); + v4l2_dbg(1, debug, sd, "write: %02x %02x\n", reg, val); for (i = 0; i < 3; i++) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } /* write regs to set audio volume etc */ -static void wm8739_set_audio(struct i2c_client *client) +static void wm8739_set_audio(struct v4l2_subdev *sd) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); u16 mute = state->muted ? 0x80 : 0; /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB * Default setting: 0x17 = 0 dB */ - wm8739_write(client, R0, (state->vol_l & 0x1f) | mute); - wm8739_write(client, R1, (state->vol_r & 0x1f) | mute); + wm8739_write(sd, R0, (state->vol_l & 0x1f) | mute); + wm8739_write(sd, R1, (state->vol_r & 0x1f) | mute); } -static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int wm8739_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -123,9 +130,9 @@ static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) return 0; } -static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int wm8739_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - struct wm8739_state *state = i2c_get_clientdata(client); + struct wm8739_state *state = to_state(sd); unsigned int work_l, work_r; switch (ctrl->id) { @@ -153,7 +160,7 @@ static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) state->vol_r = (long)work_r * 31 / 65535; /* set audio volume etc. */ - wm8739_set_audio(client); + wm8739_set_audio(sd); return 0; } @@ -192,77 +199,89 @@ static struct v4l2_queryctrl wm8739_qctrl[] = { /* ------------------------------------------------------------------------ */ -static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) +static int wm8739_s_clock_freq(struct v4l2_subdev *sd, u32 audiofreq) { - struct wm8739_state *state = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - { - u32 audiofreq = *(u32 *)arg; - - state->clock_freq = audiofreq; - /* de-activate */ - wm8739_write(client, R9, 0x000); - switch (audiofreq) { - case 44100: - /* 256fps, fs=44.1k */ - wm8739_write(client, R8, 0x020); - break; - case 48000: - /* 256fps, fs=48k */ - wm8739_write(client, R8, 0x000); - break; - case 32000: - /* 256fps, fs=32k */ - wm8739_write(client, R8, 0x018); - break; - default: - break; - } - /* activate */ - wm8739_write(client, R9, 0x001); + struct wm8739_state *state = to_state(sd); + + state->clock_freq = audiofreq; + /* de-activate */ + wm8739_write(sd, R9, 0x000); + switch (audiofreq) { + case 44100: + /* 256fps, fs=44.1k */ + wm8739_write(sd, R8, 0x020); + break; + case 48000: + /* 256fps, fs=48k */ + wm8739_write(sd, R8, 0x000); + break; + case 32000: + /* 256fps, fs=32k */ + wm8739_write(sd, R8, 0x018); + break; + default: break; } + /* activate */ + wm8739_write(sd, R9, 0x001); + return 0; +} - case VIDIOC_G_CTRL: - return wm8739_get_ctrl(client, arg); - - case VIDIOC_S_CTRL: - return wm8739_set_ctrl(client, arg); +static int wm8739_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + int i; - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) - if (qc->id && qc->id == wm8739_qctrl[i].id) { - memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); - return 0; - } - return -EINVAL; - } + for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) + if (qc->id && qc->id == wm8739_qctrl[i].id) { + memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; +} - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_WM8739, 0); +static int wm8739_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); - case VIDIOC_LOG_STATUS: - v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); - v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, - state->muted ? " (muted)" : ""); - v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f, - state->muted ? " (muted)" : ""); - break; + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8739, 0); +} - default: - return -EINVAL; - } +static int wm8739_log_status(struct v4l2_subdev *sd) +{ + struct wm8739_state *state = to_state(sd); + v4l2_info(sd, "Frequency: %u Hz\n", state->clock_freq); + v4l2_info(sd, "Volume L: %02x%s\n", state->vol_l & 0x1f, + state->muted ? " (muted)" : ""); + v4l2_info(sd, "Volume R: %02x%s\n", state->vol_r & 0x1f, + state->muted ? " (muted)" : ""); return 0; } +static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops wm8739_core_ops = { + .log_status = wm8739_log_status, + .g_chip_ident = wm8739_g_chip_ident, + .queryctrl = wm8739_queryctrl, + .g_ctrl = wm8739_g_ctrl, + .s_ctrl = wm8739_s_ctrl, +}; + +static const struct v4l2_subdev_audio_ops wm8739_audio_ops = { + .s_clock_freq = wm8739_s_clock_freq, +}; + +static const struct v4l2_subdev_ops wm8739_ops = { + .core = &wm8739_core_ops, + .audio = &wm8739_audio_ops, +}; + /* ------------------------------------------------------------------------ */ /* i2c implementation */ @@ -271,6 +290,7 @@ static int wm8739_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct wm8739_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -282,6 +302,8 @@ static int wm8739_probe(struct i2c_client *client, state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8739_ops); state->vol_l = 0x17; /* 0dB */ state->vol_r = 0x17; /* 0dB */ state->muted = 0; @@ -289,31 +311,33 @@ static int wm8739_probe(struct i2c_client *client, /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ state->volume = ((long)state->vol_l + 1) * 65535 / 31; state->clock_freq = 48000; - i2c_set_clientdata(client, state); /* Initialize wm8739 */ /* reset */ - wm8739_write(client, R15, 0x00); + wm8739_write(sd, R15, 0x00); /* filter setting, high path, offet clear */ - wm8739_write(client, R5, 0x000); + wm8739_write(sd, R5, 0x000); /* ADC, OSC, Power Off mode Disable */ - wm8739_write(client, R6, 0x000); + wm8739_write(sd, R6, 0x000); /* Digital Audio interface format: Enable Master mode, 24 bit, MSB first/left justified */ - wm8739_write(client, R7, 0x049); + wm8739_write(sd, R7, 0x049); /* sampling control: normal, 256fs, 48KHz sampling rate */ - wm8739_write(client, R8, 0x000); + wm8739_write(sd, R8, 0x000); /* activate */ - wm8739_write(client, R9, 0x001); + wm8739_write(sd, R9, 0x001); /* set volume/mute */ - wm8739_set_audio(client); + wm8739_set_audio(sd); return 0; } static int wm8739_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/drivers/media/video/wm8775.c b/linux/drivers/media/video/wm8775.c index ff7fa162b..526926635 100644 --- a/linux/drivers/media/video/wm8775.c +++ b/linux/drivers/media/video/wm8775.c @@ -32,7 +32,7 @@ #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> #include "compat.h" @@ -55,16 +55,23 @@ enum { }; struct wm8775_state { + struct v4l2_subdev sd; u8 input; /* Last selected input (0-0xf) */ u8 muted; }; -static int wm8775_write(struct i2c_client *client, int reg, u16 val) +static inline struct wm8775_state *to_state(struct v4l2_subdev *sd) { + return container_of(sd, struct wm8775_state, sd); +} + +static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); int i; if (reg < 0 || reg >= TOT_REGS) { - v4l_err(client, "Invalid register R%d\n", reg); + v4l2_err(sd, "Invalid register R%d\n", reg); return -1; } @@ -72,84 +79,117 @@ static int wm8775_write(struct i2c_client *client, int reg, u16 val) if (i2c_smbus_write_byte_data(client, (reg << 1) | (val >> 8), val & 0xff) == 0) return 0; - v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + v4l2_err(sd, "I2C: cannot write %03x to register R%d\n", val, reg); return -1; } -static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) +static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { - struct wm8775_state *state = i2c_get_clientdata(client); - struct v4l2_routing *route = arg; - struct v4l2_control *ctrl = arg; - - switch (cmd) { - case VIDIOC_INT_G_AUDIO_ROUTING: - route->input = state->input; - route->output = 0; - break; - - case VIDIOC_INT_S_AUDIO_ROUTING: - /* There are 4 inputs and one output. Zero or more inputs - are multiplexed together to the output. Hence there are - 16 combinations. - If only one input is active (the normal case) then the - input values 1, 2, 4 or 8 should be used. */ - if (route->input > 15) { - v4l_err(client, "Invalid input %d.\n", route->input); - return -EINVAL; - } - state->input = route->input; - if (state->muted) - break; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); - break; - - case VIDIOC_G_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - ctrl->value = state->muted; - break; - - case VIDIOC_S_CTRL: - if (ctrl->id != V4L2_CID_AUDIO_MUTE) - return -EINVAL; - state->muted = ctrl->value; - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - if (!state->muted) - wm8775_write(client, R21, 0x100 + state->input); - break; - - case VIDIOC_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, - arg, V4L2_IDENT_WM8775, 0); - - case VIDIOC_LOG_STATUS: - v4l_info(client, "Input: %d%s\n", state->input, - state->muted ? " (muted)" : ""); - break; - - case VIDIOC_S_FREQUENCY: - /* If I remove this, then it can happen that I have no - sound the first time I tune from static to a valid channel. - It's difficult to reproduce and is almost certainly related - to the zero cross detect circuit. */ - wm8775_write(client, R21, 0x0c0); - wm8775_write(client, R14, 0x1d4); - wm8775_write(client, R15, 0x1d4); - wm8775_write(client, R21, 0x100 + state->input); - break; - - default: + struct wm8775_state *state = to_state(sd); + + /* There are 4 inputs and one output. Zero or more inputs + are multiplexed together to the output. Hence there are + 16 combinations. + If only one input is active (the normal case) then the + input values 1, 2, 4 or 8 should be used. */ + if (route->input > 15) { + v4l2_err(sd, "Invalid input %d.\n", route->input); return -EINVAL; } + state->input = route->input; + if (state->muted) + return 0; + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wm8775_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + ctrl->value = state->muted; + return 0; +} + +static int wm8775_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct wm8775_state *state = to_state(sd); + + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + state->muted = ctrl->value; + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + if (!state->muted) + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_WM8775, 0); +} + +static int wm8775_log_status(struct v4l2_subdev *sd) +{ + struct wm8775_state *state = to_state(sd); + + v4l2_info(sd, "Input: %d%s\n", state->input, + state->muted ? " (muted)" : ""); return 0; } +static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) +{ + struct wm8775_state *state = to_state(sd); + + /* If I remove this, then it can happen that I have no + sound the first time I tune from static to a valid channel. + It's difficult to reproduce and is almost certainly related + to the zero cross detect circuit. */ + wm8775_write(sd, R21, 0x0c0); + wm8775_write(sd, R14, 0x1d4); + wm8775_write(sd, R15, 0x1d4); + wm8775_write(sd, R21, 0x100 + state->input); + return 0; +} + +static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops wm8775_core_ops = { + .log_status = wm8775_log_status, + .g_chip_ident = wm8775_g_chip_ident, + .g_ctrl = wm8775_g_ctrl, + .s_ctrl = wm8775_s_ctrl, +}; + +static const struct v4l2_subdev_tuner_ops wm8775_tuner_ops = { + .s_frequency = wm8775_s_frequency, +}; + +static const struct v4l2_subdev_audio_ops wm8775_audio_ops = { + .s_routing = wm8775_s_routing, +}; + +static const struct v4l2_subdev_ops wm8775_ops = { + .core = &wm8775_core_ops, + .tuner = &wm8775_tuner_ops, + .audio = &wm8775_audio_ops, +}; + /* ----------------------------------------------------------------------- */ /* i2c implementation */ @@ -163,56 +203,61 @@ static int wm8775_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct wm8775_state *state; + struct v4l2_subdev *sd; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -EIO; - v4l_info(client, "chip found @ 0x%x (%s)\n", + v4l_info(client, "chip found @ 0x%02x (%s)\n", client->addr << 1, client->adapter->name); state = kmalloc(sizeof(struct wm8775_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &wm8775_ops); state->input = 2; state->muted = 0; - i2c_set_clientdata(client, state); /* Initialize wm8775 */ /* RESET */ - wm8775_write(client, R23, 0x000); + wm8775_write(sd, R23, 0x000); /* Disable zero cross detect timeout */ - wm8775_write(client, R7, 0x000); + wm8775_write(sd, R7, 0x000); /* Left justified, 24-bit mode */ - wm8775_write(client, R11, 0x021); + wm8775_write(sd, R11, 0x021); /* Master mode, clock ratio 256fs */ - wm8775_write(client, R12, 0x102); + wm8775_write(sd, R12, 0x102); /* Powered up */ - wm8775_write(client, R13, 0x000); + wm8775_write(sd, R13, 0x000); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R14, 0x1d4); + wm8775_write(sd, R14, 0x1d4); /* ADC gain +2.5dB, enable zero cross */ - wm8775_write(client, R15, 0x1d4); + wm8775_write(sd, R15, 0x1d4); /* ALC Stereo, ALC target level -1dB FS max gain +8dB */ - wm8775_write(client, R16, 0x1bf); + wm8775_write(sd, R16, 0x1bf); /* Enable gain control, use zero cross detection, ALC hold time 42.6 ms */ - wm8775_write(client, R17, 0x185); + wm8775_write(sd, R17, 0x185); /* ALC gain ramp up delay 34 s, ALC gain ramp down delay 33 ms */ - wm8775_write(client, R18, 0x0a2); + wm8775_write(sd, R18, 0x0a2); /* Enable noise gate, threshold -72dBfs */ - wm8775_write(client, R19, 0x005); + wm8775_write(sd, R19, 0x005); /* Transient window 4ms, lower PGA gain limit -1dB */ - wm8775_write(client, R20, 0x07a); + wm8775_write(sd, R20, 0x07a); /* LRBOTH = 1, use input 2. */ - wm8775_write(client, R21, 0x102); + wm8775_write(sd, R21, 0x102); return 0; } static int wm8775_remove(struct i2c_client *client) { - kfree(i2c_get_clientdata(client)); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } diff --git a/linux/include/media/v4l2-common.h b/linux/include/media/v4l2-common.h index 2f8719abf..f99c866d8 100644 --- a/linux/include/media/v4l2-common.h +++ b/linux/include/media/v4l2-common.h @@ -57,6 +57,29 @@ /* ------------------------------------------------------------------------- */ +/* These printk constructs can be used with v4l2_device and v4l2_subdev */ +#define v4l2_printk(level, dev, fmt, arg...) \ + printk(level "%s: " fmt, (dev)->name , ## arg) + +#define v4l2_err(dev, fmt, arg...) \ + v4l2_printk(KERN_ERR, dev, fmt , ## arg) + +#define v4l2_warn(dev, fmt, arg...) \ + v4l2_printk(KERN_WARNING, dev, fmt , ## arg) + +#define v4l2_info(dev, fmt, arg...) \ + v4l2_printk(KERN_INFO, dev, fmt , ## arg) + +/* These three macros assume that the debug level is set with a module + parameter called 'debug'. */ +#define v4l2_dbg(level, debug, dev, fmt, arg...) \ + do { \ + if (debug >= (level)) \ + v4l2_printk(KERN_DEBUG, dev, fmt , ## arg); \ + } while (0) + +/* ------------------------------------------------------------------------- */ + /* Priority helper functions */ struct v4l2_prio_state { @@ -104,11 +127,29 @@ struct i2c_driver; struct i2c_adapter; struct i2c_client; struct i2c_device_id; +struct v4l2_device; +struct v4l2_subdev; +struct v4l2_subdev_ops; int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver, const char *name, int (*probe)(struct i2c_client *, const struct i2c_device_id *)); +/* Load an i2c module and return an initialized v4l2_subdev struct. + Only call request_module if module_name != NULL. + The client_type argument is the name of the chip that's on the adapter. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr); +/* Probe and load an i2c module and return an initialized v4l2_subdev struct. + Only call request_module if module_name != NULL. + The client_type argument is the name of the chip that's on the adapter. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + const unsigned short *addrs); +/* Initialize an v4l2_subdev with data from an i2c_client struct */ +void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, + const struct v4l2_subdev_ops *ops); + /* ------------------------------------------------------------------------- */ /* Internal ioctls */ diff --git a/linux/include/media/v4l2-device.h b/linux/include/media/v4l2-device.h new file mode 100644 index 000000000..97b283a04 --- /dev/null +++ b/linux/include/media/v4l2-device.h @@ -0,0 +1,109 @@ +/* + V4L2 device support header. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _V4L2_DEVICE_H +#define _V4L2_DEVICE_H + +#include <media/v4l2-subdev.h> + +/* Each instance of a V4L2 device should create the v4l2_device struct, + either stand-alone or embedded in a larger struct. + + It allows easy access to sub-devices (see v4l2-subdev.h) and provides + basic V4L2 device-level support. + */ + +#define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16) + +struct v4l2_device { + /* dev->driver_data points to this struct */ + struct device *dev; + /* used to keep track of the registered subdevs */ + struct list_head subdevs; + /* lock this struct; can be used by the driver as well if this + struct is embedded into a larger struct. */ + spinlock_t lock; + /* unique device name, by default the driver name + bus ID */ + char name[V4L2_DEVICE_NAME_SIZE]; +}; + +/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev */ +int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev); +/* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */ +void v4l2_device_unregister(struct v4l2_device *v4l2_dev); + +/* Register a subdev with a v4l2 device. While registered the subdev module + is marked as in-use. An error is returned if the module is no longer + loaded when you attempt to register it. */ +int __must_check v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd); +/* Unregister a subdev with a v4l2 device. Can also be called if the subdev + wasn't registered. In that case it will do nothing. */ +void v4l2_device_unregister_subdev(struct v4l2_subdev *sd); + +/* Iterate over all subdevs. */ +#define v4l2_device_for_each_subdev(sd, dev) \ + list_for_each_entry(sd, &(dev)->subdevs, list) + +/* Call the specified callback for all subdevs matching the condition. + Ignore any errors. Note that you cannot add or delete a subdev + while walking the subdevs list. */ +#define __v4l2_device_call_subdevs(dev, cond, o, f, args...) \ + do { \ + struct v4l2_subdev *sd; \ + \ + list_for_each_entry(sd, &(dev)->subdevs, list) \ + if ((cond) && sd->ops->o && sd->ops->o->f) \ + sd->ops->o->f(sd , ##args); \ + } while (0) + +/* Call the specified callback for all subdevs matching the condition. + If the callback returns an error other than 0 or -ENOIOCTLCMD, then + return with that error code. Note that you cannot add or delete a + subdev while walking the subdevs list. */ +#define __v4l2_device_call_subdevs_until_err(dev, cond, o, f, args...) \ +({ \ + struct v4l2_subdev *sd; \ + int err = 0; \ + \ + list_for_each_entry(sd, &(dev)->subdevs, list) { \ + if ((cond) && sd->ops->o && sd->ops->o->f) \ + err = sd->ops->o->f(sd , ##args); \ + if (err && err != -ENOIOCTLCMD) \ + break; \ + } \ + (err == -ENOIOCTLCMD) ? 0 : err; \ +}) + +/* Call the specified callback for all subdevs matching grp_id (if 0, then + match them all). Ignore any errors. Note that you cannot add or delete + a subdev while walking the subdevs list. */ +#define v4l2_device_call_all(dev, grp_id, o, f, args...) \ + __v4l2_device_call_subdevs(dev, \ + !(grp_id) || sd->grp_id == (grp_id), o, f , ##args) + +/* Call the specified callback for all subdevs matching grp_id (if 0, then + match them all). If the callback returns an error other than 0 or + -ENOIOCTLCMD, then return with that error code. Note that you cannot + add or delete a subdev while walking the subdevs list. */ +#define v4l2_device_call_until_err(dev, grp_id, o, f, args...) \ + __v4l2_device_call_subdevs_until_err(dev, \ + !(grp_id) || sd->grp_id == (grp_id), o, f , ##args) + +#endif diff --git a/linux/include/media/v4l2-subdev.h b/linux/include/media/v4l2-subdev.h new file mode 100644 index 000000000..bc9e0fbf2 --- /dev/null +++ b/linux/include/media/v4l2-subdev.h @@ -0,0 +1,188 @@ +/* + V4L2 sub-device support header. + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _V4L2_SUBDEV_H +#define _V4L2_SUBDEV_H + +#include <media/v4l2-common.h> + +struct v4l2_device; +struct v4l2_subdev; +struct tuner_setup; + +/* Sub-devices are devices that are connected somehow to the main bridge + device. These devices are usually audio/video muxers/encoders/decoders or + sensors and webcam controllers. + + Usually these devices are controlled through an i2c bus, but other busses + may also be used. + + The v4l2_subdev struct provides a way of accessing these devices in a + generic manner. Most operations that these sub-devices support fall in + a few categories: core ops, audio ops, video ops and tuner ops. + + More categories can be added if needed, although this should remain a + limited set (no more than approx. 8 categories). + + Each category has its own set of ops that subdev drivers can implement. + + A subdev driver can leave the pointer to the category ops NULL if + it does not implement them (e.g. an audio subdev will generally not + implement the video category ops). The exception is the core category: + this must always be present. + + These ops are all used internally so it is no problem to change, remove + or add ops or move ops from one to another category. Currently these + ops are based on the original ioctls, but since ops are not limited to + one argument there is room for improvement here once all i2c subdev + drivers are converted to use these ops. + */ + +/* Core ops: it is highly recommended to implement at least these ops: + + g_chip_ident + log_status + g_register + s_register + + This provides basic debugging support. + + The ioctl ops is meant for generic ioctl-like commands. Depending on + the use-case it might be better to use subdev-specific ops (currently + not yet implemented) since ops provide proper type-checking. + */ +struct v4l2_subdev_core_ops { + int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_chip_ident *chip); + int (*log_status)(struct v4l2_subdev *sd); + int (*init)(struct v4l2_subdev *sd, u32 val); + int (*s_standby)(struct v4l2_subdev *sd, u32 standby); + int (*reset)(struct v4l2_subdev *sd, u32 val); + int (*s_gpio)(struct v4l2_subdev *sd, u32 val); + int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc); + int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl); + int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); + int (*ioctl)(struct v4l2_subdev *sd, int cmd, void *arg); +#ifdef CONFIG_VIDEO_ADV_DEBUG + int (*g_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); + int (*s_register)(struct v4l2_subdev *sd, struct v4l2_register *reg); +#endif +}; + +struct v4l2_subdev_tuner_ops { + int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type); + int (*s_radio)(struct v4l2_subdev *sd); + int (*s_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); + int (*g_frequency)(struct v4l2_subdev *sd, struct v4l2_frequency *freq); + int (*g_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); + int (*s_tuner)(struct v4l2_subdev *sd, struct v4l2_tuner *vt); + int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm); + int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type); + int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config); +}; + +struct v4l2_subdev_audio_ops { + int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq); + int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq); + int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route); +}; + +struct v4l2_subdev_video_ops { + int (*s_routing)(struct v4l2_subdev *sd, const struct v4l2_routing *route); + int (*s_crystal_freq)(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq); + int (*decode_vbi_line)(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi_line); + int (*s_vbi_data)(struct v4l2_subdev *sd, const struct v4l2_sliced_vbi_data *vbi_data); + int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data); + int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std); + int (*s_stream)(struct v4l2_subdev *sd, int enable); + int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); + int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt); +}; + +struct v4l2_subdev_ops { + const struct v4l2_subdev_core_ops *core; + const struct v4l2_subdev_tuner_ops *tuner; + const struct v4l2_subdev_audio_ops *audio; + const struct v4l2_subdev_video_ops *video; +}; + +#define V4L2_SUBDEV_NAME_SIZE 32 + +/* Each instance of a subdev driver should create this struct, either + stand-alone or embedded in a larger struct. + */ +struct v4l2_subdev { + struct list_head list; + struct module *owner; + struct v4l2_device *dev; + const struct v4l2_subdev_ops *ops; + /* name must be unique */ + char name[V4L2_SUBDEV_NAME_SIZE]; + /* can be used to group similar subdevs, value is driver-specific */ + u32 grp_id; + /* pointer to private data */ + void *priv; +}; + +static inline void v4l2_set_subdevdata(struct v4l2_subdev *sd, void *p) +{ + sd->priv = p; +} + +static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd) +{ + return sd->priv; +} + +/* Convert an ioctl-type command to the proper v4l2_subdev_ops function call. + This is used by subdev modules that can be called by both old-style ioctl + commands and through the v4l2_subdev_ops. + + The ioctl API of the subdev driver can call this function to call the + right ops based on the ioctl cmd and arg. + + Once all subdev drivers have been converted and all drivers no longer + use the ioctl interface, then this function can be removed. + */ +int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg); + +static inline void v4l2_subdev_init(struct v4l2_subdev *sd, + const struct v4l2_subdev_ops *ops) +{ + INIT_LIST_HEAD(&sd->list); + /* ops->core MUST be set */ + BUG_ON(!ops || !ops->core); + sd->ops = ops; + sd->dev = NULL; + sd->name[0] = '\0'; + sd->grp_id = 0; + sd->priv = NULL; +} + +/* Call an ops of a v4l2_subdev, doing the right checks against + NULL pointers. + + Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip); + */ +#define v4l2_subdev_call(sd, o, f, args...) \ + (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \ + (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD)) + +#endif |