Skip to content

Commit

Permalink
V4L/DVB: Add SPI support to V4L2
Browse files Browse the repository at this point in the history
Add support SPI bus to v4l2. Useful for control some device with SPI bus like
hardware MPEG2 encoders and etc.

Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com>
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Dmitri Belimov authored and Mauro Carvalho Chehab committed May 18, 2010
1 parent 443fed9 commit 85e0921
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 0 deletions.
63 changes: 63 additions & 0 deletions drivers/media/video/v4l2-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#if defined(CONFIG_SPI)
#include <linux/spi/spi.h>
#endif
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/pgtable.h>
Expand Down Expand Up @@ -955,6 +958,66 @@ EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);

#endif /* defined(CONFIG_I2C) */

#if defined(CONFIG_SPI)

/* Load a spi sub-device. */

void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
const struct v4l2_subdev_ops *ops)
{
v4l2_subdev_init(sd, ops);
sd->flags |= V4L2_SUBDEV_FL_IS_SPI;
/* the owner is the same as the spi_device's driver owner */
sd->owner = spi->dev.driver->owner;
/* spi_device and v4l2_subdev point to one another */
v4l2_set_subdevdata(sd, spi);
spi_set_drvdata(spi, sd);
/* initialize name */
strlcpy(sd->name, spi->dev.driver->name, sizeof(sd->name));
}
EXPORT_SYMBOL_GPL(v4l2_spi_subdev_init);

struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
struct spi_master *master, struct spi_board_info *info)
{
struct v4l2_subdev *sd = NULL;
struct spi_device *spi = NULL;

BUG_ON(!v4l2_dev);

if (info->modalias)
request_module(info->modalias);

spi = spi_new_device(master, info);

if (spi == NULL || spi->dev.driver == NULL)
goto error;

if (!try_module_get(spi->dev.driver->owner))
goto error;

sd = spi_get_drvdata(spi);

/* Register with the v4l2_device which increases the module's
use count as well. */
if (v4l2_device_register_subdev(v4l2_dev, sd))
sd = NULL;

/* Decrease the module use count to match the first try_module_get. */
module_put(spi->dev.driver->owner);

error:
/* If we have a client but no subdev, then something went wrong and
we must unregister the client. */
if (spi && sd == NULL)
spi_unregister_device(spi);

return sd;
}
EXPORT_SYMBOL_GPL(v4l2_spi_new_subdev);

#endif /* defined(CONFIG_SPI) */

/* Clamp x to be between min and max, aligned to a multiple of 2^align. min
* and max don't have to be aligned, but there must be at least one valid
* value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples
Expand Down
11 changes: 11 additions & 0 deletions drivers/media/video/v4l2-device.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#include <linux/types.h>
#include <linux/ioctl.h>
#include <linux/i2c.h>
#if defined(CONFIG_SPI)
#include <linux/spi/spi.h>
#endif
#include <linux/videodev2.h>
#include <media/v4l2-device.h>

Expand Down Expand Up @@ -96,6 +99,14 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
if (client)
i2c_unregister_device(client);
}
#endif
#if defined(CONFIG_SPI)
if (sd->flags & V4L2_SUBDEV_FL_IS_SPI) {
struct spi_device *spi = v4l2_get_subdevdata(sd);

if (spi)
spi_unregister_device(spi);
}
#endif
}
}
Expand Down
19 changes: 19 additions & 0 deletions include/media/v4l2-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,25 @@ const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type);

/* ------------------------------------------------------------------------- */

/* SPI Helper functions */
#if defined(CONFIG_SPI)

#include <linux/spi/spi.h>

struct spi_device;

/* Load an spi module and return an initialized v4l2_subdev struct.
The client_type argument is the name of the chip that's on the adapter. */
struct v4l2_subdev *v4l2_spi_new_subdev(struct v4l2_device *v4l2_dev,
struct spi_master *master, struct spi_board_info *info);

/* Initialize an v4l2_subdev with data from an spi_device struct */
void v4l2_spi_subdev_init(struct v4l2_subdev *sd, struct spi_device *spi,
const struct v4l2_subdev_ops *ops);
#endif

/* ------------------------------------------------------------------------- */

/* Note: these remaining ioctls/structs should be removed as well, but they are
still used in tuner-simple.c (TUNER_SET_CONFIG), cx18/ivtv (RESET) and
v4l2-int-device.h (v4l2_routing). To remove these ioctls some more cleanup
Expand Down
2 changes: 2 additions & 0 deletions include/media/v4l2-subdev.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,8 @@ struct v4l2_subdev_ops {

/* Set this flag if this subdev is a i2c device. */
#define V4L2_SUBDEV_FL_IS_I2C (1U << 0)
/* Set this flag if this subdev is a spi device. */
#define V4L2_SUBDEV_FL_IS_SPI (1U << 1)

/* Each instance of a subdev driver should create this struct, either
stand-alone or embedded in a larger struct.
Expand Down

0 comments on commit 85e0921

Please sign in to comment.