Skip to content

Commit

Permalink
V4L/DVB (11032): mt9m001: allow setting of bus width from board code
Browse files Browse the repository at this point in the history
This patch removes the phytec specific setting of the bus width
and switches to the more generic query_bus_param/set_bus_param
hooks

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Sascha Hauer authored and Mauro Carvalho Chehab committed Mar 30, 2009
1 parent d75b1dc commit 36034dc
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 110 deletions.
7 changes: 0 additions & 7 deletions drivers/media/video/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -707,13 +707,6 @@ config SOC_CAMERA_MT9M001
This driver supports MT9M001 cameras from Micron, monochrome
and colour models.

config MT9M001_PCA9536_SWITCH
bool "pca9536 datawidth switch for mt9m001"
depends on SOC_CAMERA_MT9M001 && GENERIC_GPIO
help
Select this if your MT9M001 camera uses a PCA9536 I2C GPIO
extender to switch between 8 and 10 bit datawidth modes

config SOC_CAMERA_MT9M111
tristate "mt9m111 and mt9m112 support"
depends on SOC_CAMERA && I2C
Expand Down
143 changes: 40 additions & 103 deletions drivers/media/video/mt9m001.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/gpio.h>

#include <media/v4l2-common.h>
#include <media/v4l2-chip-ident.h>
Expand Down Expand Up @@ -73,9 +72,7 @@ struct mt9m001 {
struct i2c_client *client;
struct soc_camera_device icd;
int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
int switch_gpio;
unsigned char autoexposure;
unsigned char datawidth;
};

static int reg_read(struct soc_camera_device *icd, const u8 reg)
Expand Down Expand Up @@ -181,105 +178,43 @@ static int mt9m001_stop_capture(struct soc_camera_device *icd)
return 0;
}

static int bus_switch_request(struct mt9m001 *mt9m001,
struct soc_camera_link *icl)
{
#ifdef CONFIG_MT9M001_PCA9536_SWITCH
int ret;
unsigned int gpio = icl->gpio;

if (gpio_is_valid(gpio)) {
/* We have a data bus switch. */
ret = gpio_request(gpio, "mt9m001");
if (ret < 0) {
dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n",
gpio);
return ret;
}

ret = gpio_direction_output(gpio, 0);
if (ret < 0) {
dev_err(&mt9m001->client->dev,
"Cannot set GPIO %u to output\n", gpio);
gpio_free(gpio);
return ret;
}
}

mt9m001->switch_gpio = gpio;
#else
mt9m001->switch_gpio = -EINVAL;
#endif
return 0;
}

static void bus_switch_release(struct mt9m001 *mt9m001)
{
#ifdef CONFIG_MT9M001_PCA9536_SWITCH
if (gpio_is_valid(mt9m001->switch_gpio))
gpio_free(mt9m001->switch_gpio);
#endif
}

static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit)
{
#ifdef CONFIG_MT9M001_PCA9536_SWITCH
if (!gpio_is_valid(mt9m001->switch_gpio))
return -ENODEV;

gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit);
return 0;
#else
return -ENODEV;
#endif
}

static int bus_switch_possible(struct mt9m001 *mt9m001)
{
#ifdef CONFIG_MT9M001_PCA9536_SWITCH
return gpio_is_valid(mt9m001->switch_gpio);
#else
return 0;
#endif
}

static int mt9m001_set_bus_param(struct soc_camera_device *icd,
unsigned long flags)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
int ret;
struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;

/* Flags validity verified in test_bus_param */
/* Only one width bit may be set */
if (!is_power_of_2(width_flag))
return -EINVAL;

if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) ||
(mt9m001->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) ||
(mt9m001->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) {
/* Well, we actually only can do 10 or 8 bits... */
if (width_flag == SOCAM_DATAWIDTH_9)
return -EINVAL;
ret = bus_switch_act(mt9m001,
width_flag == SOCAM_DATAWIDTH_8);
if (ret < 0)
return ret;
if (icl->set_bus_param)
return icl->set_bus_param(icl, width_flag);

mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10;
}
/*
* Without board specific bus width settings we only support the
* sensors native bus width
*/
if (width_flag == SOCAM_DATAWIDTH_10)
return 0;

return 0;
return -EINVAL;
}

static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
{
struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
/* MT9M001 has all capture_format parameters fixed */
unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING |
unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;

if (bus_switch_possible(mt9m001))
flags |= SOCAM_DATAWIDTH_8;
if (icl->query_bus_param)
flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
else
flags |= SOCAM_DATAWIDTH_10;

return soc_camera_apply_sensor_flags(icl, flags);
}
Expand Down Expand Up @@ -598,6 +533,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
s32 data;
int ret;
unsigned long flags;

/* We must have a parent by now. And it cannot be a wrong one.
* So this entire test is completely redundant. */
Expand All @@ -618,18 +554,10 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
case 0x8421:
mt9m001->model = V4L2_IDENT_MT9M001C12ST;
icd->formats = mt9m001_colour_formats;
if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats);
else
icd->num_formats = 1;
break;
case 0x8431:
mt9m001->model = V4L2_IDENT_MT9M001C12STM;
icd->formats = mt9m001_monochrome_formats;
if (gpio_is_valid(icl->gpio))
icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats);
else
icd->num_formats = 1;
break;
default:
ret = -ENODEV;
Expand All @@ -638,6 +566,26 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
goto ei2c;
}

icd->num_formats = 0;

/*
* This is a 10bit sensor, so by default we only allow 10bit.
* The platform may support different bus widths due to
* different routing of the data lines.
*/
if (icl->query_bus_param)
flags = icl->query_bus_param(icl);
else
flags = SOCAM_DATAWIDTH_10;

if (flags & SOCAM_DATAWIDTH_10)
icd->num_formats++;
else
icd->formats++;

if (flags & SOCAM_DATAWIDTH_8)
icd->num_formats++;

dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
data == 0x8431 ? "C12STM" : "C12ST");

Expand Down Expand Up @@ -703,27 +651,17 @@ static int mt9m001_probe(struct i2c_client *client,
icd->height_max = 1024;
icd->y_skip_top = 1;
icd->iface = icl->bus_id;
/* Default datawidth - this is the only width this camera (normally)
* supports. It is only with extra logic that it can support
* other widths. Therefore it seems to be a sensible default. */
mt9m001->datawidth = 10;
/* Simulated autoexposure. If enabled, we calculate shutter width
* ourselves in the driver based on vertical blanking and frame width */
mt9m001->autoexposure = 1;

ret = bus_switch_request(mt9m001, icl);
if (ret)
goto eswinit;

ret = soc_camera_device_register(icd);
if (ret)
goto eisdr;

return 0;

eisdr:
bus_switch_release(mt9m001);
eswinit:
kfree(mt9m001);
return ret;
}
Expand All @@ -733,7 +671,6 @@ static int mt9m001_remove(struct i2c_client *client)
struct mt9m001 *mt9m001 = i2c_get_clientdata(client);

soc_camera_device_unregister(&mt9m001->icd);
bus_switch_release(mt9m001);
kfree(mt9m001);

return 0;
Expand Down

0 comments on commit 36034dc

Please sign in to comment.