diff options
Diffstat (limited to 'patches.tizen/0729-V4L-s5c73m3-Add-device-tree-support.patch')
-rw-r--r-- | patches.tizen/0729-V4L-s5c73m3-Add-device-tree-support.patch | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/patches.tizen/0729-V4L-s5c73m3-Add-device-tree-support.patch b/patches.tizen/0729-V4L-s5c73m3-Add-device-tree-support.patch new file mode 100644 index 00000000000..ab37c3ac8b4 --- /dev/null +++ b/patches.tizen/0729-V4L-s5c73m3-Add-device-tree-support.patch @@ -0,0 +1,537 @@ +From 8e760838daba94a919df0ebe73252da2fa46466b Mon Sep 17 00:00:00 2001 +From: Andrzej Hajda <a.hajda@samsung.com> +Date: Fri, 12 Jul 2013 11:11:10 +0200 +Subject: [PATCH 0729/1302] V4L: s5c73m3: Add device tree support + +This patch adds the V4L2 asynchronous subdev registration and +device tree support. Common clock API is used to control the +sensor master clock from within the subdev driver. + +Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> +Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com> +Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> +--- +Changes since v1: + - added missing ret assignment on clk_get failure, + - fixed error paths in probe(), + - changed clock name to cis_extclk as specified in the datasheet, + - separate properties used for the XSHUTDOWN, STANDBY GPIOs, + - multiple correctiond in the binding documentation (improved + description of data-lanes and clock-frequency properties). + +Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com> +--- + .../devicetree/bindings/media/samsung-s5c73m3.txt | 95 ++++++++++ + drivers/media/i2c/s5c73m3/s5c73m3-core.c | 206 ++++++++++++++++----- + drivers/media/i2c/s5c73m3/s5c73m3-spi.c | 6 + + drivers/media/i2c/s5c73m3/s5c73m3.h | 4 + + 4 files changed, 261 insertions(+), 50 deletions(-) + create mode 100644 Documentation/devicetree/bindings/media/samsung-s5c73m3.txt + +diff --git a/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt +new file mode 100644 +index 0000000..6d6c0b6 +--- /dev/null ++++ b/Documentation/devicetree/bindings/media/samsung-s5c73m3.txt +@@ -0,0 +1,95 @@ ++Samsung S5C73M3 8Mp camera ISP ++------------------------------ ++ ++The S5C73M3 camera ISP supports MIPI CSI-2 and parallel (ITU-R BT.656) video ++data busses. The I2C bus is the main control bus and additionally the SPI bus ++is used, mostly for transferring the firmware to and from the device. Two ++slave device nodes corresponding to these control bus interfaces are required ++and should be placed under respective bus controller nodes. ++ ++I2C slave device node ++--------------------- ++ ++Required properties: ++ ++- compatible : "samsung,s5c73m3"; ++- reg : I2C slave address of the sensor; ++- vdd-int-supply : digital power supply (1.2V); ++- vdda-supply : analog power supply (1.2V); ++- vdd-reg-supply : regulator input power supply (2.8V); ++- vddio-host-supply : host I/O power supply (1.8V to 2.8V); ++- vddio-cis-supply : CIS I/O power supply (1.2V to 1.8V); ++- vdd-af-supply : lens power supply (2.8V); ++- xshutdown-gpios : specifier of GPIO connected to the XSHUTDOWN pin; ++- standby-gpios : specifier of GPIO connected to the STANDBY pin; ++- clocks : contains the sensor's CIS_EXTCLK clock specifier; ++- clock-names : contains "cis_extclk" entry; ++ ++Optional properties: ++ ++- clock-frequency : the frequency at which the "cis_extclk" clock should be ++ configured to operate, in Hz; if this property is not ++ specified default 24 MHz value will be used. ++ ++The common video interfaces bindings (see video-interfaces.txt) should be used ++to specify link from the S5C73M3 to an external image data receiver. The S5C73M3 ++device node should contain one 'port' child node with an 'endpoint' subnode for ++this purpose. The data link from a raw image sensor to the S5C73M3 can be ++similarly specified, but it is optional since the S5C73M3 ISP and a raw image ++sensor are usually inseparable and form a hybrid module. ++ ++Following properties are valid for the endpoint node(s): ++ ++endpoint subnode ++---------------- ++ ++- data-lanes : (optional) specifies MIPI CSI-2 data lanes as covered in ++ video-interfaces.txt. This sensor doesn't support data lane remapping ++ and physical lane indexes in subsequent elements of the array should ++ be only consecutive ascending values. ++ ++SPI device node ++--------------- ++ ++Required properties: ++ ++- compatible : "samsung,s5c73m3"; ++ ++For more details see description of the SPI busses bindings ++(../spi/spi-bus.txt) and bindings of a specific bus controller. ++ ++Example: ++ ++i2c@138A000000 { ++ ... ++ s5c73m3@3c { ++ compatible = "samsung,s5c73m3"; ++ reg = <0x3c>; ++ vdd-int-supply = <&buck9_reg>; ++ vdda-supply = <&ldo17_reg>; ++ vdd-reg-supply = <&cam_io_reg>; ++ vddio-host-supply = <&ldo18_reg>; ++ vddio-cis-supply = <&ldo9_reg>; ++ vdd-af-supply = <&cam_af_reg>; ++ clock-frequency = <24000000>; ++ clocks = <&clk 0>; ++ clock-names = "cis_extclk"; ++ reset-gpios = <&gpf1 3 1>; ++ standby-gpios = <&gpm0 1 1>; ++ port { ++ s5c73m3_ep: endpoint { ++ remote-endpoint = <&csis0_ep>; ++ data-lanes = <1 2 3 4>; ++ }; ++ }; ++ }; ++}; ++ ++spi@1392000 { ++ ... ++ s5c73m3_spi: s5c73m3 { ++ compatible = "samsung,s5c73m3"; ++ reg = <0>; ++ ... ++ }; ++}; +diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c +index 45ca106..6583f88 100644 +--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c ++++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c +@@ -15,7 +15,7 @@ + * GNU General Public License for more details. + */ + +-#include <linux/sizes.h> ++#include <linux/clk.h> + #include <linux/delay.h> + #include <linux/firmware.h> + #include <linux/gpio.h> +@@ -23,7 +23,9 @@ + #include <linux/init.h> + #include <linux/media.h> + #include <linux/module.h> ++#include <linux/of_gpio.h> + #include <linux/regulator/consumer.h> ++#include <linux/sizes.h> + #include <linux/slab.h> + #include <linux/spi/spi.h> + #include <linux/videodev2.h> +@@ -33,6 +35,7 @@ + #include <media/v4l2-subdev.h> + #include <media/v4l2-mediabus.h> + #include <media/s5c73m3.h> ++#include <media/v4l2-of.h> + + #include "s5c73m3.h" + +@@ -47,6 +50,8 @@ module_param(update_fw, int, 0644); + + #define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K + #define S5C73M3_SPI_TX_SIZE (16 * SZ_1K) ++#define S5C73M3_MIPI_DATA_LANES 4 ++#define S5C73M3_CLK_NAME "cis_extclk" + + static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = { + "vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ +@@ -1356,9 +1361,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state) + for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) { + ret = regulator_enable(state->supplies[i].consumer); + if (ret) +- goto err; ++ goto err_reg_dis; + } + ++ ret = clk_set_rate(state->clock, state->mclk_frequency); ++ if (ret < 0) ++ goto err_reg_dis; ++ ++ ret = clk_prepare_enable(state->clock); ++ if (ret < 0) ++ goto err_reg_dis; ++ ++ v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n", ++ clk_get_rate(state->clock)); ++ + s5c73m3_gpio_deassert(state, STBY); + usleep_range(100, 200); + +@@ -1366,7 +1382,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state) + usleep_range(50, 100); + + return 0; +-err: ++ ++err_reg_dis: + for (--i; i >= 0; i--) + regulator_disable(state->supplies[i].consumer); + return ret; +@@ -1381,6 +1398,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state) + + if (s5c73m3_gpio_assert(state, STBY)) + usleep_range(100, 200); ++ ++ clk_disable_unprepare(state->clock); ++ + state->streaming = 0; + state->isp_ready = 0; + +@@ -1389,6 +1409,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state) + if (ret) + goto err; + } ++ + return 0; + err: + for (++i; i < S5C73M3_MAX_SUPPLIES; i++) { +@@ -1405,6 +1426,7 @@ static int s5c73m3_oif_set_power(struct v4l2_subdev *sd, int on) + struct s5c73m3 *state = oif_sd_to_s5c73m3(sd); + int ret = 0; + ++ + mutex_lock(&state->lock); + + if (on && !state->power) { +@@ -1452,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) + S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + +- mutex_lock(&state->lock); +- ret = __s5c73m3_power_on(state); +- if (ret == 0) +- s5c73m3_get_fw_version(state); +- +- __s5c73m3_power_off(state); +- mutex_unlock(&state->lock); +- +- v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", +- __func__, ret ? "failed" : "succeded", ret); +- + return ret; + } + +@@ -1520,41 +1531,108 @@ static const struct v4l2_subdev_ops oif_subdev_ops = { + .video = &s5c73m3_oif_video_ops, + }; + +-static int s5c73m3_configure_gpios(struct s5c73m3 *state, +- const struct s5c73m3_platform_data *pdata) ++static int s5c73m3_configure_gpios(struct s5c73m3 *state) ++{ ++ static const char * const gpio_names[] = { ++ "S5C73M3_STBY", "S5C73M3_RST" ++ }; ++ struct i2c_client *c = state->i2c_client; ++ struct s5c73m3_gpio *g = state->gpio; ++ int ret, i; ++ ++ for (i = 0; i < GPIO_NUM; ++i) { ++ unsigned int flags = GPIOF_DIR_OUT; ++ if (g[i].level) ++ flags |= GPIOF_INIT_HIGH; ++ ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, ++ gpio_names[i]); ++ if (ret) { ++ v4l2_err(c, "failed to request gpio %s\n", ++ gpio_names[i]); ++ return ret; ++ } ++ } ++ return 0; ++} ++ ++static int s5c73m3_parse_gpios(struct s5c73m3 *state) ++{ ++ static const char * const prop_names[] = { ++ "standby-gpios", "xshutdown-gpios", ++ }; ++ struct device *dev = &state->i2c_client->dev; ++ struct device_node *node = dev->of_node; ++ int ret, i; ++ ++ for (i = 0; i < GPIO_NUM; ++i) { ++ enum of_gpio_flags of_flags; ++ ++ ret = of_get_named_gpio_flags(node, prop_names[i], ++ 0, &of_flags); ++ if (ret < 0) { ++ dev_err(dev, "failed to parse %s DT property\n", ++ prop_names[i]); ++ return -EINVAL; ++ } ++ state->gpio[i].gpio = ret; ++ state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW); ++ } ++ return 0; ++} ++ ++static int s5c73m3_get_platform_data(struct s5c73m3 *state) + { + struct device *dev = &state->i2c_client->dev; +- const struct s5c73m3_gpio *gpio; +- unsigned long flags; ++ const struct s5c73m3_platform_data *pdata = dev->platform_data; ++ struct device_node *node = dev->of_node; ++ struct device_node *node_ep; ++ struct v4l2_of_endpoint ep; + int ret; + +- state->gpio[STBY].gpio = -EINVAL; +- state->gpio[RST].gpio = -EINVAL; ++ if (!node) { ++ if (!pdata) { ++ dev_err(dev, "Platform data not specified\n"); ++ return -EINVAL; ++ } + +- gpio = &pdata->gpio_stby; +- if (gpio_is_valid(gpio->gpio)) { +- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) +- | GPIOF_EXPORT; +- ret = devm_gpio_request_one(dev, gpio->gpio, flags, +- "S5C73M3_STBY"); +- if (ret < 0) +- return ret; ++ state->mclk_frequency = pdata->mclk_frequency; ++ state->gpio[STBY] = pdata->gpio_stby; ++ state->gpio[RST] = pdata->gpio_reset; ++ return 0; ++ } + +- state->gpio[STBY] = *gpio; ++ if (of_property_read_u32(node, "clock-frequency", ++ &state->mclk_frequency)) { ++ state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ; ++ dev_info(dev, "using default %u Hz clock frequency\n", ++ state->mclk_frequency); + } + +- gpio = &pdata->gpio_reset; +- if (gpio_is_valid(gpio->gpio)) { +- flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) +- | GPIOF_EXPORT; +- ret = devm_gpio_request_one(dev, gpio->gpio, flags, +- "S5C73M3_RST"); +- if (ret < 0) +- return ret; ++ ret = s5c73m3_parse_gpios(state); ++ if (ret < 0) ++ return -EINVAL; + +- state->gpio[RST] = *gpio; ++ node_ep = v4l2_of_get_next_endpoint(node, NULL); ++ if (!node_ep) { ++ dev_warn(dev, "no endpoint defined for node: %s\n", ++ node->full_name); ++ return 0; + } + ++ v4l2_of_parse_endpoint(node_ep, &ep); ++ of_node_put(node_ep); ++ ++ if (ep.bus_type != V4L2_MBUS_CSI2) { ++ dev_err(dev, "unsupported bus type: %#x\n", ep.bus_type); ++ return -EINVAL; ++ } ++ /* ++ * Number of MIPI CSI-2 data lanes is currently not configurable, ++ * always a default value of 4 lanes is used. ++ */ ++ if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES) ++ dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n"); ++ + return 0; + } + +@@ -1562,21 +1640,24 @@ static int s5c73m3_probe(struct i2c_client *client, + const struct i2c_device_id *id) + { + struct device *dev = &client->dev; +- const struct s5c73m3_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct v4l2_subdev *oif_sd; + struct s5c73m3 *state; + int ret, i; + +- if (pdata == NULL) { +- dev_err(&client->dev, "Platform data not specified\n"); +- return -EINVAL; +- } +- + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + ++ state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME); ++ if (IS_ERR(state->clock)) ++ return -EPROBE_DEFER; ++ ++ state->i2c_client = client; ++ ret = s5c73m3_get_platform_data(state); ++ if (ret < 0) ++ return ret; ++ + mutex_init(&state->lock); + sd = &state->sensor_sd; + oif_sd = &state->oif_sd; +@@ -1614,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client, + if (ret < 0) + return ret; + +- state->mclk_frequency = pdata->mclk_frequency; +- state->bus_type = pdata->bus_type; +- state->i2c_client = client; +- +- ret = s5c73m3_configure_gpios(state, pdata); ++ ret = s5c73m3_configure_gpios(state); + if (ret) + goto out_err; + +@@ -1656,9 +1733,29 @@ static int s5c73m3_probe(struct i2c_client *client, + state->frame_desc.entry[0].length = 10 * SZ_1M; + state->frame_desc.entry[1].length = S5C73M3_EMBEDDED_DATA_MAXLEN; + ++ oif_sd->dev = dev; ++ ++ ret = __s5c73m3_power_on(state); ++ if (ret < 0) ++ goto out_err1; ++ ++ ret = s5c73m3_get_fw_version(state); ++ __s5c73m3_power_off(state); ++ ++ if (ret < 0) { ++ dev_err(dev, "Device detection failed: %d\n", ret); ++ goto out_err1; ++ } ++ ++ ret = v4l2_async_register_subdev(oif_sd); ++ if (ret < 0) ++ goto out_err1; ++ + v4l2_info(sd, "%s: completed succesfully\n", __func__); + return 0; + ++out_err1: ++ s5c73m3_unregister_spi_driver(state); + out_err: + media_entity_cleanup(&sd->entity); + return ret; +@@ -1670,7 +1767,7 @@ static int s5c73m3_remove(struct i2c_client *client) + struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd); + struct v4l2_subdev *sensor_sd = &state->sensor_sd; + +- v4l2_device_unregister_subdev(oif_sd); ++ v4l2_async_unregister_subdev(oif_sd); + + v4l2_ctrl_handler_free(oif_sd->ctrl_handler); + media_entity_cleanup(&oif_sd->entity); +@@ -1689,8 +1786,17 @@ static const struct i2c_device_id s5c73m3_id[] = { + }; + MODULE_DEVICE_TABLE(i2c, s5c73m3_id); + ++#ifdef CONFIG_OF ++static const struct of_device_id s5c73m3_of_match[] = { ++ { .compatible = "samsung,s5c73m3" }, ++ { } ++}; ++MODULE_DEVICE_TABLE(of, s5c73m3_of_match); ++#endif ++ + static struct i2c_driver s5c73m3_i2c_driver = { + .driver = { ++ .of_match_table = of_match_ptr(s5c73m3_of_match), + .name = DRIVER_NAME, + }, + .probe = s5c73m3_probe, +diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +index be44558..323b47b 100644 +--- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c ++++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +@@ -27,6 +27,11 @@ + + #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" + ++static const struct of_device_id s5c73m3_spi_ids[] = { ++ { .compatible = "samsung,s5c73m3" }, ++ { } ++}; ++ + enum spi_direction { + SPI_DIR_RX, + SPI_DIR_TX +@@ -141,6 +146,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state) + spidrv->driver.name = S5C73M3_SPI_DRV_NAME; + spidrv->driver.bus = &spi_bus_type; + spidrv->driver.owner = THIS_MODULE; ++ spidrv->driver.of_match_table = s5c73m3_spi_ids; + + return spi_register_driver(spidrv); + } +diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h +index 9d2c086..2917857 100644 +--- a/drivers/media/i2c/s5c73m3/s5c73m3.h ++++ b/drivers/media/i2c/s5c73m3/s5c73m3.h +@@ -17,6 +17,7 @@ + #ifndef S5C73M3_H_ + #define S5C73M3_H_ + ++#include <linux/clk.h> + #include <linux/kernel.h> + #include <linux/regulator/consumer.h> + #include <media/v4l2-common.h> +@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads { + + + #define S5C73M3_MAX_SUPPLIES 6 ++#define S5C73M3_DEFAULT_MCLK_FREQ 24000000U + + struct s5c73m3_ctrls { + struct v4l2_ctrl_handler handler; +@@ -391,6 +393,8 @@ struct s5c73m3 { + struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES]; + struct s5c73m3_gpio gpio[GPIO_NUM]; + ++ struct clk *clock; ++ + /* External master clock frequency */ + u32 mclk_frequency; + /* Video bus type - MIPI-CSI2/paralell */ +-- +1.8.3.2 + |