[RFC/PATCH 09/13] media: s5k6aa: Add support for device tree based instantiation

Sylwester Nawrocki s.nawrocki at samsung.com
Sat May 26 05:52:48 EST 2012


The driver initializes all board related properties except the s_power()
callback to board code. The platforms that require this callback are not
supported by this driver yet for CONFIG_OF=y.

Signed-off-by: Sylwester Nawrocki <s.nawrocki at samsung.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie at samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
---
 .../bindings/camera/samsung-s5k6aafx.txt           |   57 +++++++++
 drivers/media/video/s5k6aa.c                       |  129 ++++++++++++++------
 2 files changed, 146 insertions(+), 40 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/camera/samsung-s5k6aafx.txt

diff --git a/Documentation/devicetree/bindings/camera/samsung-s5k6aafx.txt b/Documentation/devicetree/bindings/camera/samsung-s5k6aafx.txt
new file mode 100644
index 0000000..6685a9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/camera/samsung-s5k6aafx.txt
@@ -0,0 +1,57 @@
+Samsung S5K6AAFX camera sensor
+------------------------------
+
+Required properties:
+
+- compatible : "samsung,s5k6aafx";
+- reg : base address of the device on I2C bus;
+- video-itu-601-bus : parallel bus with HSYNC and VSYNC - ITU-R BT.601;
+- vdd_core-supply : digital core voltage supply 1.5V (1.4V to 1.6V);
+- vdda-supply : analog power voltage supply 2.8V (2.6V to 3.0V);
+- vdd_reg-supply : regulator input power voltage supply 1.8V (1.7V to 1.9V)
+		   or 2.8V (2.6V to 3.0);
+- vddio-supply : I/O voltage supply 1.8V (1.65V to 1.95V)
+		 or 2.8V (2.5V to 3.1V);
+
+Optional properties:
+
+- clock-frequency : the IP's main (system bus) clock frequency in Hz, the default
+		    value when this property is not specified is 24 MHz;
+- data-lanes : number of physical lanes used (default 2 if not specified);
+- gpio-stby : specifies the S5K6AA_STBY GPIO
+- gpio-rst : specifies the S5K6AA_RST GPIO
+- samsung,s5k6aa-inv-stby : set inverted S5K6AA_STBY GPIO level;
+- samsung,s5k6aa-inv-rst : set inverted S5K6AA_RST GPIO level;
+- samsung,s5k6aa-hflip : set the default horizontal image flipping;
+- samsung,s5k6aa-vflip : set the default vertical image flipping;
+
+
+Example:
+
+	gpl2: gpio-controller at 0 {
+	};
+
+	reg0: regulator at 0 {
+	};
+
+	reg1: regulator at 1 {
+	};
+
+	reg2: regulator at 2 {
+	};
+
+	reg3: regulator at 3 {
+	};
+
+	s5k6aafx at 3c {
+		compatible = "samsung,s5k6aafx";
+		reg = <0x3c>;
+		clock-frequency = <24000000>;
+		gpio-rst = <&gpl2 0 2 0 3>;
+		gpio-stby = <&gpl2 1 2 0 3>;
+		video-itu-601-bus;
+		vdd_core-supply = <&reg0>;
+		vdda-supply = <&reg1>;
+		vdd_reg-supply = <&reg2>;
+		vddio-supply = <&reg3>;
+	};
diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c
index 6625e46..ed172bb 100644
--- a/drivers/media/video/s5k6aa.c
+++ b/drivers/media/video/s5k6aa.c
@@ -20,6 +20,7 @@
 #include <linux/i2c.h>
 #include <linux/media.h>
 #include <linux/module.h>
+#include <linux/of_gpio.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
@@ -232,14 +233,14 @@ struct s5k6aa {
 	struct media_pad pad;
 
 	enum v4l2_mbus_type bus_type;
-	u8 mipi_lanes;
+	u32 mipi_lanes;
 
 	int (*s_power)(int enable);
 	struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES];
 	struct s5k6aa_gpio gpio[GPIO_NUM];
 
 	/* external master clock frequency */
-	unsigned long mclk_frequency;
+	u32 mclk_frequency;
 	/* ISP internal master clock frequency */
 	u16 clk_fop;
 	/* output pixel clock frequency range */
@@ -1519,68 +1520,109 @@ static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa)
 	}
 }
 
-static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa,
-				  const struct s5k6aa_platform_data *pdata)
+static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, struct device *dev)
 {
-	const struct s5k6aa_gpio *gpio = &pdata->gpio_stby;
+	const struct s5k6aa_platform_data *pdata = dev->platform_data;
+	struct device_node *np = dev->of_node;
+	const struct s5k6aa_gpio *pgpio;
+	struct s5k6aa_gpio gpio = { 0 };
 	int ret;
 
 	s5k6aa->gpio[STBY].gpio = -EINVAL;
 	s5k6aa->gpio[RST].gpio  = -EINVAL;
 
-	ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY");
+	if (np) {
+		gpio.gpio = of_get_named_gpio(np, "gpio-stby", 0);
+		if (!of_get_property(np, "samsung,s5k6aa-inv-stby", NULL))
+			gpio.level = 1;
+	}
+	pgpio = np ? &gpio : &pdata->gpio_stby;
+	ret = s5k6aa_configure_gpio(pgpio->gpio, pgpio->level, "S5K6AA_STBY");
 	if (ret) {
 		s5k6aa_free_gpios(s5k6aa);
 		return ret;
 	}
-	s5k6aa->gpio[STBY] = *gpio;
-	if (gpio_is_valid(gpio->gpio))
-		gpio_set_value(gpio->gpio, 0);
+	s5k6aa->gpio[STBY] = *pgpio;
 
-	gpio = &pdata->gpio_reset;
-	ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST");
-	if (ret) {
-		s5k6aa_free_gpios(s5k6aa);
-		return ret;
+	if (np) {
+		gpio.gpio = of_get_named_gpio(np, "gpio-rst", 0);
+		if (!of_get_property(np, "samsung,s5k6aa-inv-rst", NULL))
+			gpio.level = 1;
+	}
+	pgpio = np ? &gpio : &pdata->gpio_reset;
+	ret = s5k6aa_configure_gpio(pgpio->gpio, pgpio->level, "S5K6AA_RST");
+	if (ret)
+		goto err;
+
+	s5k6aa->gpio[RST] = *pgpio;
+	return 0;
+ err:
+	s5k6aa_free_gpios(s5k6aa);
+	return ret;
+}
+
+static int s5k6aa_get_platform_data(struct s5k6aa *s5k6aa,
+				    struct i2c_client *client)
+{
+	const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
+	struct device_node *np = client->dev.of_node;
+	const char *bus_type;
+
+	if (np == NULL) {
+		if (pdata == NULL) {
+			dev_err(&client->dev, "Platform data not specified\n");
+			return -EINVAL;
+		}
+		s5k6aa->mclk_frequency = pdata->mclk_frequency;
+		s5k6aa->bus_type = pdata->bus_type;
+		s5k6aa->mipi_lanes = pdata->nlanes;
+		s5k6aa->s_power	= pdata->set_power;
+		s5k6aa->inv_hflip = pdata->horiz_flip;
+		s5k6aa->inv_vflip = pdata->vert_flip;
+		return 0;
 	}
-	s5k6aa->gpio[RST] = *gpio;
-	if (gpio_is_valid(gpio->gpio))
-		gpio_set_value(gpio->gpio, 0);
 
+	if (of_property_read_u32(np, "clock-frequency",
+				  &s5k6aa->mclk_frequency))
+		s5k6aa->mclk_frequency = 24000000UL;
+
+	if (of_property_read_u32(np, "data-lanes", &s5k6aa->mipi_lanes))
+		s5k6aa->mipi_lanes = 2;
+
+	if (!of_property_read_string(np, "video-bus-type", &bus_type) &&
+	    !strcmp(bus_type, "mipi-csi2"))
+		s5k6aa->bus_type = V4L2_MBUS_CSI2;
+	else
+		s5k6aa->bus_type = V4L2_MBUS_PARALLEL;
+
+	s5k6aa->inv_hflip = of_property_read_bool(np, "samsung,s5k6aa-hflip");
+	s5k6aa->inv_vflip = of_property_read_bool(np, "samsung,s5k6aa-vflip");
 	return 0;
 }
 
+
 static int s5k6aa_probe(struct i2c_client *client,
 			const struct i2c_device_id *id)
 {
-	const struct s5k6aa_platform_data *pdata = client->dev.platform_data;
 	struct v4l2_subdev *sd;
 	struct s5k6aa *s5k6aa;
-	int i, ret;
+	int i, ret = -EINVAL;
 
-	if (pdata == NULL) {
-		dev_err(&client->dev, "Platform data not specified\n");
-		return -EINVAL;
-	}
+	s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL);
+	if (!s5k6aa)
+		return -ENOMEM;
 
-	if (pdata->mclk_frequency == 0) {
+	ret = s5k6aa_get_platform_data(s5k6aa, client);
+	if (ret < 0)
+		return ret;
+
+	if (s5k6aa->mclk_frequency == 0) {
 		dev_err(&client->dev, "MCLK frequency not specified\n");
 		return -EINVAL;
 	}
 
-	s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL);
-	if (!s5k6aa)
-		return -ENOMEM;
-
 	mutex_init(&s5k6aa->lock);
 
-	s5k6aa->mclk_frequency = pdata->mclk_frequency;
-	s5k6aa->bus_type = pdata->bus_type;
-	s5k6aa->mipi_lanes = pdata->nlanes;
-	s5k6aa->s_power	= pdata->set_power;
-	s5k6aa->inv_hflip = pdata->horiz_flip;
-	s5k6aa->inv_vflip = pdata->vert_flip;
-
 	sd = &s5k6aa->sd;
 	v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
 	strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
@@ -1592,9 +1634,9 @@ static int s5k6aa_probe(struct i2c_client *client,
 	sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR;
 	ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0);
 	if (ret)
-		goto out_err1;
+		return ret;
 
-	ret = s5k6aa_configure_gpios(s5k6aa, pdata);
+	ret = s5k6aa_configure_gpios(s5k6aa, &client->dev);
 	if (ret)
 		goto out_err2;
 
@@ -1627,8 +1669,6 @@ out_err3:
 	s5k6aa_free_gpios(s5k6aa);
 out_err2:
 	media_entity_cleanup(&s5k6aa->sd.entity);
-out_err1:
-	kfree(s5k6aa);
 	return ret;
 }
 
@@ -1642,7 +1682,6 @@ static int s5k6aa_remove(struct i2c_client *client)
 	media_entity_cleanup(&sd->entity);
 	regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies);
 	s5k6aa_free_gpios(s5k6aa);
-	kfree(s5k6aa);
 
 	return 0;
 }
@@ -1653,9 +1692,18 @@ static const struct i2c_device_id s5k6aa_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, s5k6aa_id);
 
+#ifdef CONFIG_OF
+static const struct of_device_id s5k6aa_of_match[] __devinitconst = {
+	{ .compatible = "samsung,s5k6aafx" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, s5k6aa_of_match);
+#endif
+
 
 static struct i2c_driver s5k6aa_i2c_driver = {
 	.driver = {
+		.of_match_table = of_match_ptr(s5k6aa_of_match),
 		.name = DRIVER_NAME
 	},
 	.probe		= s5k6aa_probe,
@@ -1665,6 +1713,7 @@ static struct i2c_driver s5k6aa_i2c_driver = {
 
 module_i2c_driver(s5k6aa_i2c_driver);
 
+MODULE_ALIAS("i2c:s5k6aafx");
 MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
 MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki at samsung.com>");
 MODULE_LICENSE("GPL");
-- 
1.7.10



More information about the devicetree-discuss mailing list