[media] s5p-csis: Add device tree support

This patch support for binding the driver to the MIPI-CSIS
devices instantiated from device tree and parsing the SoC
and board specific properties. The MIPI CSI-2 channel is
determined by the value of reg property placed in csis'
port subnode.

Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Sylwester Nawrocki 2013-03-26 08:20:30 -03:00 committed by Mauro Carvalho Chehab
parent ee12b04910
commit 02399e35e6
4 changed files with 215 additions and 35 deletions

View File

@ -0,0 +1,81 @@
Samsung S5P/EXYNOS SoC series MIPI CSI-2 receiver (MIPI CSIS)
-------------------------------------------------------------
Required properties:
- compatible : "samsung,s5pv210-csis" for S5PV210 (S5PC110),
"samsung,exynos4210-csis" for Exynos4210 (S5PC210),
"samsung,exynos4212-csis" for Exynos4212/Exynos4412
SoC series;
- reg : offset and length of the register set for the device;
- interrupts : should contain MIPI CSIS interrupt; the format of the
interrupt specifier depends on the interrupt controller;
- bus-width : maximum number of data lanes supported (SoC specific);
- vddio-supply : MIPI CSIS I/O and PLL voltage supply (e.g. 1.8V);
- vddcore-supply : MIPI CSIS Core voltage supply (e.g. 1.1V);
- clocks : list of clock specifiers, corresponding to entries in
clock-names property;
- clock-names : must contain "csis", "sclk_csis" entries, matching entries
in the clocks property.
Optional properties:
- clock-frequency : The IP's main (system bus) clock frequency in Hz, default
value when this property is not specified is 166 MHz;
- samsung,csis-wclk : CSI-2 wrapper clock selection. If this property is present
external clock from CMU will be used, or the bus clock if
if it's not specified.
The device node should contain one 'port' child node with one child 'endpoint'
node, according to the bindings defined in Documentation/devicetree/bindings/
media/video-interfaces.txt. The following are properties specific to those nodes.
port node
---------
- reg : (required) must be 3 for camera C input (CSIS0) or 4 for
camera D input (CSIS1);
endpoint node
-------------
- data-lanes : (required) an array specifying active physical MIPI-CSI2
data input lanes and their mapping to logical lanes; the
array's content is unused, only its length is meaningful;
- samsung,csis-hs-settle : (optional) differential receiver (HS-RX) settle time;
Example:
reg0: regulator@0 {
};
reg1: regulator@1 {
};
/* SoC properties */
csis_0: csis@11880000 {
compatible = "samsung,exynos4210-csis";
reg = <0x11880000 0x1000>;
interrupts = <0 78 0>;
#address-cells = <1>;
#size-cells = <0>;
};
/* Board properties */
csis_0: csis@11880000 {
clock-frequency = <166000000>;
vddio-supply = <&reg0>;
vddcore-supply = <&reg1>;
port {
reg = <3>; /* 3 - CSIS0, 4 - CSIS1 */
csis0_ep: endpoint {
remote-endpoint = <...>;
data-lanes = <1>, <2>;
samsung,csis-hs-settle = <12>;
};
};
};

View File

@ -19,14 +19,18 @@
#include <linux/kernel.h>
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_data/mipi-csis.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/videodev2.h>
#include <media/s5p_fimc.h>
#include <media/v4l2-of.h>
#include <media/v4l2-subdev.h>
#include <linux/platform_data/mipi-csis.h>
#include "mipi-csis.h"
static int debug;
@ -113,6 +117,7 @@ static char *csi_clock_name[] = {
[CSIS_CLK_GATE] = "csis",
};
#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
static const char * const csis_supply_name[] = {
"vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */
@ -167,6 +172,11 @@ struct csis_pktbuf {
* @clock: CSIS clocks
* @irq: requested s5p-mipi-csis irq number
* @flags: the state variable for power and streaming control
* @clock_frequency: device bus clock frequency
* @hs_settle: HS-RX settle time
* @num_lanes: number of MIPI-CSI data lanes used
* @max_num_lanes: maximum number of MIPI-CSI data lanes supported
* @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
* @csis_fmt: current CSIS pixel format
* @format: common media bus format for the source and sink pad
* @slock: spinlock protecting structure members below
@ -184,6 +194,13 @@ struct csis_state {
struct clk *clock[NUM_CSIS_CLOCKS];
int irq;
u32 flags;
u32 clk_frequency;
u32 hs_settle;
u32 num_lanes;
u32 max_num_lanes;
u8 wclk_ext;
const struct csis_pix_format *csis_fmt;
struct v4l2_mbus_framefmt format;
@ -273,7 +290,6 @@ static void s5pcsis_reset(struct csis_state *state)
static void s5pcsis_system_enable(struct csis_state *state, int on)
{
struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
u32 val, mask;
val = s5pcsis_read(state, S5PCSIS_CTRL);
@ -286,7 +302,7 @@ static void s5pcsis_system_enable(struct csis_state *state, int on)
val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
val &= ~S5PCSIS_DPHYCTRL_ENABLE;
if (on) {
mask = (1 << (pdata->lanes + 1)) - 1;
mask = (1 << (state->num_lanes + 1)) - 1;
val |= (mask & S5PCSIS_DPHYCTRL_ENABLE);
}
s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
@ -321,15 +337,14 @@ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
static void s5pcsis_set_params(struct csis_state *state)
{
struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
u32 val;
val = s5pcsis_read(state, S5PCSIS_CONFIG);
val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1);
val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1);
s5pcsis_write(state, S5PCSIS_CONFIG, val);
__s5pcsis_set_format(state);
s5pcsis_set_hsync_settle(state, pdata->hs_settle);
s5pcsis_set_hsync_settle(state, state->hs_settle);
val = s5pcsis_read(state, S5PCSIS_CTRL);
if (state->csis_fmt->data_alignment == 32)
@ -338,7 +353,7 @@ static void s5pcsis_set_params(struct csis_state *state)
val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
if (pdata->wclk_source)
if (state->wclk_ext)
val |= S5PCSIS_CTRL_WCLK_EXTCLK;
s5pcsis_write(state, S5PCSIS_CTRL, val);
@ -701,52 +716,111 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int s5pcsis_get_platform_data(struct platform_device *pdev,
struct csis_state *state)
{
struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "Platform data not specified\n");
return -EINVAL;
}
state->clk_frequency = pdata->clk_rate;
state->num_lanes = pdata->lanes;
state->hs_settle = pdata->hs_settle;
state->index = max(0, pdev->id);
state->max_num_lanes = state->index ? CSIS1_MAX_LANES :
CSIS0_MAX_LANES;
return 0;
}
#ifdef CONFIG_OF
static int s5pcsis_parse_dt(struct platform_device *pdev,
struct csis_state *state)
{
struct device_node *node = pdev->dev.of_node;
struct v4l2_of_endpoint endpoint;
if (of_property_read_u32(node, "clock-frequency",
&state->clk_frequency))
state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
if (of_property_read_u32(node, "bus-width",
&state->max_num_lanes))
return -EINVAL;
node = v4l2_of_get_next_endpoint(node, NULL);
if (!node) {
dev_err(&pdev->dev, "No port node at %s\n",
node->full_name);
return -EINVAL;
}
/* Get port node and validate MIPI-CSI channel id. */
v4l2_of_parse_endpoint(node, &endpoint);
state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0;
if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)
return -ENXIO;
/* Get MIPI CSI-2 bus configration from the endpoint node. */
of_property_read_u32(node, "samsung,csis-hs-settle",
&state->hs_settle);
state->wclk_ext = of_property_read_bool(node,
"samsung,csis-wclk");
state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
of_node_put(node);
return 0;
}
#else
#define s5pcsis_parse_dt(pdev, state) (-ENOSYS)
#endif
static int s5pcsis_probe(struct platform_device *pdev)
{
struct s5p_platform_mipi_csis *pdata;
struct device *dev = &pdev->dev;
struct resource *mem_res;
struct csis_state *state;
int ret = -ENOMEM;
int i;
state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
mutex_init(&state->lock);
spin_lock_init(&state->slock);
state->pdev = pdev;
state->index = max(0, pdev->id);
pdata = pdev->dev.platform_data;
if (pdata == NULL) {
dev_err(&pdev->dev, "Platform data not fully specified\n");
return -EINVAL;
}
if (dev->of_node)
ret = s5pcsis_parse_dt(pdev, state);
else
ret = s5pcsis_get_platform_data(pdev, state);
if (ret < 0)
return ret;
if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
pdata->lanes > CSIS0_MAX_LANES) {
dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
pdata->lanes);
if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) {
dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n",
state->num_lanes, state->max_num_lanes);
return -EINVAL;
}
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
state->regs = devm_ioremap_resource(&pdev->dev, mem_res);
state->regs = devm_ioremap_resource(dev, mem_res);
if (IS_ERR(state->regs))
return PTR_ERR(state->regs);
state->irq = platform_get_irq(pdev, 0);
if (state->irq < 0) {
dev_err(&pdev->dev, "Failed to get irq\n");
dev_err(dev, "Failed to get irq\n");
return state->irq;
}
for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
state->supplies[i].supply = csis_supply_name[i];
ret = devm_regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES,
state->supplies);
if (ret)
return ret;
@ -755,11 +829,11 @@ static int s5pcsis_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
if (pdata->clk_rate)
if (state->clk_frequency)
ret = clk_set_rate(state->clock[CSIS_CLK_MUX],
pdata->clk_rate);
state->clk_frequency);
else
dev_WARN(&pdev->dev, "No clock frequency specified!\n");
dev_WARN(dev, "No clock frequency specified!\n");
if (ret < 0)
goto e_clkput;
@ -767,16 +841,17 @@ static int s5pcsis_probe(struct platform_device *pdev)
if (ret < 0)
goto e_clkput;
ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
0, dev_name(&pdev->dev), state);
ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler,
0, dev_name(dev), state);
if (ret) {
dev_err(&pdev->dev, "Interrupt request failed\n");
dev_err(dev, "Interrupt request failed\n");
goto e_clkdis;
}
v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
state->sd.owner = THIS_MODULE;
strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name));
snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d",
CSIS_SUBDEV_NAME, state->index);
state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
state->csis_fmt = &s5pcsis_formats[0];
@ -796,10 +871,12 @@ static int s5pcsis_probe(struct platform_device *pdev)
/* .. and a pointer to the subdev. */
platform_set_drvdata(pdev, &state->sd);
memcpy(state->events, s5pcsis_events, sizeof(state->events));
pm_runtime_enable(dev);
pm_runtime_enable(&pdev->dev);
dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",
state->num_lanes, state->hs_settle, state->wclk_ext,
state->clk_frequency);
return 0;
e_clkdis:
@ -923,13 +1000,21 @@ static const struct dev_pm_ops s5pcsis_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
};
static const struct of_device_id s5pcsis_of_match[] = {
{ .compatible = "samsung,s5pv210-csis" },
{ .compatible = "samsung,exynos4210-csis" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, s5pcsis_of_match);
static struct platform_driver s5pcsis_driver = {
.probe = s5pcsis_probe,
.remove = s5pcsis_remove,
.driver = {
.name = CSIS_DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &s5pcsis_pm_ops,
.of_match_table = s5pcsis_of_match,
.name = CSIS_DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &s5pcsis_pm_ops,
},
};

View File

@ -11,6 +11,7 @@
#define S5P_MIPI_CSIS_H_
#define CSIS_DRIVER_NAME "s5p-mipi-csis"
#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
#define CSIS_MAX_ENTITIES 2
#define CSIS0_MAX_LANES 4
#define CSIS1_MAX_LANES 2

View File

@ -14,6 +14,19 @@
#include <media/media-entity.h>
/*
* Enumeration of data inputs to the camera subsystem.
*/
enum fimc_input {
FIMC_INPUT_PARALLEL_0 = 1,
FIMC_INPUT_PARALLEL_1,
FIMC_INPUT_MIPI_CSI2_0 = 3,
FIMC_INPUT_MIPI_CSI2_1,
FIMC_INPUT_WRITEBACK_A = 5,
FIMC_INPUT_WRITEBACK_B,
FIMC_INPUT_WRITEBACK_ISP = 5,
};
/*
* Enumeration of the FIMC data bus types.
*/