Commit 594bb46d authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Mauro Carvalho Chehab

V4L/DVB (11607): soc-camera: add a free_bus method to struct soc_camera_link

Currently pcm990 camera bus-width management functions request a GPIO and never
free it again. With this approach the GPIO extender driver cannot be unloaded
once camera drivers have been loaded, also unloading theb i2c-pxa bus driver
produces errors, because the GPIO extender driver cannot unregister properly.
Another problem is, that if camera drivers are once loaded before the GPIO
extender driver, the platform code marks the GPIO unavailable and only a reboot
helps to recover. Adding an explicit free_bus method and using it in mt9m001
and mt9v022 drivers fixes these problems.
Signed-off-by: default avatarGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Acked-by: default avatarEric Miao <eric.miao@marvell.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 84d728c3
...@@ -380,12 +380,12 @@ static struct pca953x_platform_data pca9536_data = { ...@@ -380,12 +380,12 @@ static struct pca953x_platform_data pca9536_data = {
.gpio_base = NR_BUILTIN_GPIO, .gpio_base = NR_BUILTIN_GPIO,
}; };
static int gpio_bus_switch; static int gpio_bus_switch = -EINVAL;
static int pcm990_camera_set_bus_param(struct soc_camera_link *link, static int pcm990_camera_set_bus_param(struct soc_camera_link *link,
unsigned long flags) unsigned long flags)
{ {
if (gpio_bus_switch <= 0) { if (gpio_bus_switch < 0) {
if (flags == SOCAM_DATAWIDTH_10) if (flags == SOCAM_DATAWIDTH_10)
return 0; return 0;
else else
...@@ -404,25 +404,34 @@ static unsigned long pcm990_camera_query_bus_param(struct soc_camera_link *link) ...@@ -404,25 +404,34 @@ static unsigned long pcm990_camera_query_bus_param(struct soc_camera_link *link)
{ {
int ret; int ret;
if (!gpio_bus_switch) { if (gpio_bus_switch < 0) {
ret = gpio_request(NR_BUILTIN_GPIO, "camera"); ret = gpio_request(NR_BUILTIN_GPIO, "camera");
if (!ret) { if (!ret) {
gpio_bus_switch = NR_BUILTIN_GPIO; gpio_bus_switch = NR_BUILTIN_GPIO;
gpio_direction_output(gpio_bus_switch, 0); gpio_direction_output(gpio_bus_switch, 0);
} else }
gpio_bus_switch = -EINVAL;
} }
if (gpio_bus_switch > 0) if (gpio_bus_switch >= 0)
return SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_10; return SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_10;
else else
return SOCAM_DATAWIDTH_10; return SOCAM_DATAWIDTH_10;
} }
static void pcm990_camera_free_bus(struct soc_camera_link *link)
{
if (gpio_bus_switch < 0)
return;
gpio_free(gpio_bus_switch);
gpio_bus_switch = -EINVAL;
}
static struct soc_camera_link iclink = { static struct soc_camera_link iclink = {
.bus_id = 0, /* Must match with the camera ID above */ .bus_id = 0, /* Must match with the camera ID above */
.query_bus_param = pcm990_camera_query_bus_param, .query_bus_param = pcm990_camera_query_bus_param,
.set_bus_param = pcm990_camera_set_bus_param, .set_bus_param = pcm990_camera_set_bus_param,
.free_bus = pcm990_camera_free_bus,
}; };
/* Board I2C devices. */ /* Board I2C devices. */
......
...@@ -604,10 +604,13 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) ...@@ -604,10 +604,13 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
static void mt9m001_video_remove(struct soc_camera_device *icd) static void mt9m001_video_remove(struct soc_camera_device *icd)
{ {
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr, dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr,
icd->dev.parent, icd->vdev); icd->dev.parent, icd->vdev);
soc_camera_video_stop(icd); soc_camera_video_stop(icd);
if (icl->free_bus)
icl->free_bus(icl);
} }
static int mt9m001_probe(struct i2c_client *client, static int mt9m001_probe(struct i2c_client *client,
......
...@@ -735,10 +735,13 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) ...@@ -735,10 +735,13 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
static void mt9v022_video_remove(struct soc_camera_device *icd) static void mt9v022_video_remove(struct soc_camera_device *icd)
{ {
struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr, dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr,
icd->dev.parent, icd->vdev); icd->dev.parent, icd->vdev);
soc_camera_video_stop(icd); soc_camera_video_stop(icd);
if (icl->free_bus)
icl->free_bus(icl);
} }
static int mt9v022_probe(struct i2c_client *client, static int mt9v022_probe(struct i2c_client *client,
......
...@@ -107,6 +107,7 @@ struct soc_camera_link { ...@@ -107,6 +107,7 @@ struct soc_camera_link {
*/ */
int (*set_bus_param)(struct soc_camera_link *, unsigned long flags); int (*set_bus_param)(struct soc_camera_link *, unsigned long flags);
unsigned long (*query_bus_param)(struct soc_camera_link *); unsigned long (*query_bus_param)(struct soc_camera_link *);
void (*free_bus)(struct soc_camera_link *);
}; };
static inline struct soc_camera_device *to_soc_camera_dev(struct device *dev) static inline struct soc_camera_device *to_soc_camera_dev(struct device *dev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment