aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform
diff options
context:
space:
mode:
authorSylwester Nawrocki <s.nawrocki@samsung.com>2013-03-26 07:20:30 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2013-03-31 09:42:58 -0400
commit02399e35e6bb716ce9636eba006b792362270034 (patch)
tree24ec7af43a7ee0c310bf6dd64457396875f7d268 /drivers/media/platform
parentee12b049104118a58ac13da207a84c867191b17a (diff)
[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>
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/s5p-fimc/mipi-csis.c155
-rw-r--r--drivers/media/platform/s5p-fimc/mipi-csis.h1
2 files changed, 121 insertions, 35 deletions
diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c
index 981863d05aaa..8636bcddde1b 100644
--- a/drivers/media/platform/s5p-fimc/mipi-csis.c
+++ b/drivers/media/platform/s5p-fimc/mipi-csis.c
@@ -19,14 +19,18 @@
19#include <linux/kernel.h> 19#include <linux/kernel.h>
20#include <linux/memory.h> 20#include <linux/memory.h>
21#include <linux/module.h> 21#include <linux/module.h>
22#include <linux/of.h>
23#include <linux/platform_data/mipi-csis.h>
22#include <linux/platform_device.h> 24#include <linux/platform_device.h>
23#include <linux/pm_runtime.h> 25#include <linux/pm_runtime.h>
24#include <linux/regulator/consumer.h> 26#include <linux/regulator/consumer.h>
25#include <linux/slab.h> 27#include <linux/slab.h>
26#include <linux/spinlock.h> 28#include <linux/spinlock.h>
27#include <linux/videodev2.h> 29#include <linux/videodev2.h>
30#include <media/s5p_fimc.h>
31#include <media/v4l2-of.h>
28#include <media/v4l2-subdev.h> 32#include <media/v4l2-subdev.h>
29#include <linux/platform_data/mipi-csis.h> 33
30#include "mipi-csis.h" 34#include "mipi-csis.h"
31 35
32static int debug; 36static int debug;
@@ -113,6 +117,7 @@ static char *csi_clock_name[] = {
113 [CSIS_CLK_GATE] = "csis", 117 [CSIS_CLK_GATE] = "csis",
114}; 118};
115#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) 119#define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name)
120#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
116 121
117static const char * const csis_supply_name[] = { 122static const char * const csis_supply_name[] = {
118 "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ 123 "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */
@@ -167,6 +172,11 @@ struct csis_pktbuf {
167 * @clock: CSIS clocks 172 * @clock: CSIS clocks
168 * @irq: requested s5p-mipi-csis irq number 173 * @irq: requested s5p-mipi-csis irq number
169 * @flags: the state variable for power and streaming control 174 * @flags: the state variable for power and streaming control
175 * @clock_frequency: device bus clock frequency
176 * @hs_settle: HS-RX settle time
177 * @num_lanes: number of MIPI-CSI data lanes used
178 * @max_num_lanes: maximum number of MIPI-CSI data lanes supported
179 * @wclk_ext: CSI wrapper clock: 0 - bus clock, 1 - external SCLK_CAM
170 * @csis_fmt: current CSIS pixel format 180 * @csis_fmt: current CSIS pixel format
171 * @format: common media bus format for the source and sink pad 181 * @format: common media bus format for the source and sink pad
172 * @slock: spinlock protecting structure members below 182 * @slock: spinlock protecting structure members below
@@ -184,6 +194,13 @@ struct csis_state {
184 struct clk *clock[NUM_CSIS_CLOCKS]; 194 struct clk *clock[NUM_CSIS_CLOCKS];
185 int irq; 195 int irq;
186 u32 flags; 196 u32 flags;
197
198 u32 clk_frequency;
199 u32 hs_settle;
200 u32 num_lanes;
201 u32 max_num_lanes;
202 u8 wclk_ext;
203
187 const struct csis_pix_format *csis_fmt; 204 const struct csis_pix_format *csis_fmt;
188 struct v4l2_mbus_framefmt format; 205 struct v4l2_mbus_framefmt format;
189 206
@@ -273,7 +290,6 @@ static void s5pcsis_reset(struct csis_state *state)
273 290
274static void s5pcsis_system_enable(struct csis_state *state, int on) 291static void s5pcsis_system_enable(struct csis_state *state, int on)
275{ 292{
276 struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
277 u32 val, mask; 293 u32 val, mask;
278 294
279 val = s5pcsis_read(state, S5PCSIS_CTRL); 295 val = s5pcsis_read(state, S5PCSIS_CTRL);
@@ -286,7 +302,7 @@ static void s5pcsis_system_enable(struct csis_state *state, int on)
286 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL); 302 val = s5pcsis_read(state, S5PCSIS_DPHYCTRL);
287 val &= ~S5PCSIS_DPHYCTRL_ENABLE; 303 val &= ~S5PCSIS_DPHYCTRL_ENABLE;
288 if (on) { 304 if (on) {
289 mask = (1 << (pdata->lanes + 1)) - 1; 305 mask = (1 << (state->num_lanes + 1)) - 1;
290 val |= (mask & S5PCSIS_DPHYCTRL_ENABLE); 306 val |= (mask & S5PCSIS_DPHYCTRL_ENABLE);
291 } 307 }
292 s5pcsis_write(state, S5PCSIS_DPHYCTRL, val); 308 s5pcsis_write(state, S5PCSIS_DPHYCTRL, val);
@@ -321,15 +337,14 @@ static void s5pcsis_set_hsync_settle(struct csis_state *state, int settle)
321 337
322static void s5pcsis_set_params(struct csis_state *state) 338static void s5pcsis_set_params(struct csis_state *state)
323{ 339{
324 struct s5p_platform_mipi_csis *pdata = state->pdev->dev.platform_data;
325 u32 val; 340 u32 val;
326 341
327 val = s5pcsis_read(state, S5PCSIS_CONFIG); 342 val = s5pcsis_read(state, S5PCSIS_CONFIG);
328 val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (pdata->lanes - 1); 343 val = (val & ~S5PCSIS_CFG_NR_LANE_MASK) | (state->num_lanes - 1);
329 s5pcsis_write(state, S5PCSIS_CONFIG, val); 344 s5pcsis_write(state, S5PCSIS_CONFIG, val);
330 345
331 __s5pcsis_set_format(state); 346 __s5pcsis_set_format(state);
332 s5pcsis_set_hsync_settle(state, pdata->hs_settle); 347 s5pcsis_set_hsync_settle(state, state->hs_settle);
333 348
334 val = s5pcsis_read(state, S5PCSIS_CTRL); 349 val = s5pcsis_read(state, S5PCSIS_CTRL);
335 if (state->csis_fmt->data_alignment == 32) 350 if (state->csis_fmt->data_alignment == 32)
@@ -338,7 +353,7 @@ static void s5pcsis_set_params(struct csis_state *state)
338 val &= ~S5PCSIS_CTRL_ALIGN_32BIT; 353 val &= ~S5PCSIS_CTRL_ALIGN_32BIT;
339 354
340 val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; 355 val &= ~S5PCSIS_CTRL_WCLK_EXTCLK;
341 if (pdata->wclk_source) 356 if (state->wclk_ext)
342 val |= S5PCSIS_CTRL_WCLK_EXTCLK; 357 val |= S5PCSIS_CTRL_WCLK_EXTCLK;
343 s5pcsis_write(state, S5PCSIS_CTRL, val); 358 s5pcsis_write(state, S5PCSIS_CTRL, val);
344 359
@@ -701,52 +716,111 @@ static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id)
701 return IRQ_HANDLED; 716 return IRQ_HANDLED;
702} 717}
703 718
719static int s5pcsis_get_platform_data(struct platform_device *pdev,
720 struct csis_state *state)
721{
722 struct s5p_platform_mipi_csis *pdata = pdev->dev.platform_data;
723
724 if (pdata == NULL) {
725 dev_err(&pdev->dev, "Platform data not specified\n");
726 return -EINVAL;
727 }
728
729 state->clk_frequency = pdata->clk_rate;
730 state->num_lanes = pdata->lanes;
731 state->hs_settle = pdata->hs_settle;
732 state->index = max(0, pdev->id);
733 state->max_num_lanes = state->index ? CSIS1_MAX_LANES :
734 CSIS0_MAX_LANES;
735 return 0;
736}
737
738#ifdef CONFIG_OF
739static int s5pcsis_parse_dt(struct platform_device *pdev,
740 struct csis_state *state)
741{
742 struct device_node *node = pdev->dev.of_node;
743 struct v4l2_of_endpoint endpoint;
744
745 if (of_property_read_u32(node, "clock-frequency",
746 &state->clk_frequency))
747 state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
748 if (of_property_read_u32(node, "bus-width",
749 &state->max_num_lanes))
750 return -EINVAL;
751
752 node = v4l2_of_get_next_endpoint(node, NULL);
753 if (!node) {
754 dev_err(&pdev->dev, "No port node at %s\n",
755 node->full_name);
756 return -EINVAL;
757 }
758 /* Get port node and validate MIPI-CSI channel id. */
759 v4l2_of_parse_endpoint(node, &endpoint);
760
761 state->index = endpoint.port - FIMC_INPUT_MIPI_CSI2_0;
762 if (state->index < 0 || state->index >= CSIS_MAX_ENTITIES)
763 return -ENXIO;
764
765 /* Get MIPI CSI-2 bus configration from the endpoint node. */
766 of_property_read_u32(node, "samsung,csis-hs-settle",
767 &state->hs_settle);
768 state->wclk_ext = of_property_read_bool(node,
769 "samsung,csis-wclk");
770
771 state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
772
773 of_node_put(node);
774 return 0;
775}
776#else
777#define s5pcsis_parse_dt(pdev, state) (-ENOSYS)
778#endif
779
704static int s5pcsis_probe(struct platform_device *pdev) 780static int s5pcsis_probe(struct platform_device *pdev)
705{ 781{
706 struct s5p_platform_mipi_csis *pdata; 782 struct device *dev = &pdev->dev;
707 struct resource *mem_res; 783 struct resource *mem_res;
708 struct csis_state *state; 784 struct csis_state *state;
709 int ret = -ENOMEM; 785 int ret = -ENOMEM;
710 int i; 786 int i;
711 787
712 state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL); 788 state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
713 if (!state) 789 if (!state)
714 return -ENOMEM; 790 return -ENOMEM;
715 791
716 mutex_init(&state->lock); 792 mutex_init(&state->lock);
717 spin_lock_init(&state->slock); 793 spin_lock_init(&state->slock);
718
719 state->pdev = pdev; 794 state->pdev = pdev;
720 state->index = max(0, pdev->id);
721 795
722 pdata = pdev->dev.platform_data; 796 if (dev->of_node)
723 if (pdata == NULL) { 797 ret = s5pcsis_parse_dt(pdev, state);
724 dev_err(&pdev->dev, "Platform data not fully specified\n"); 798 else
725 return -EINVAL; 799 ret = s5pcsis_get_platform_data(pdev, state);
726 } 800 if (ret < 0)
801 return ret;
727 802
728 if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) || 803 if (state->num_lanes == 0 || state->num_lanes > state->max_num_lanes) {
729 pdata->lanes > CSIS0_MAX_LANES) { 804 dev_err(dev, "Unsupported number of data lanes: %d (max. %d)\n",
730 dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", 805 state->num_lanes, state->max_num_lanes);
731 pdata->lanes);
732 return -EINVAL; 806 return -EINVAL;
733 } 807 }
734 808
735 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 809 mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
736 state->regs = devm_ioremap_resource(&pdev->dev, mem_res); 810 state->regs = devm_ioremap_resource(dev, mem_res);
737 if (IS_ERR(state->regs)) 811 if (IS_ERR(state->regs))
738 return PTR_ERR(state->regs); 812 return PTR_ERR(state->regs);
739 813
740 state->irq = platform_get_irq(pdev, 0); 814 state->irq = platform_get_irq(pdev, 0);
741 if (state->irq < 0) { 815 if (state->irq < 0) {
742 dev_err(&pdev->dev, "Failed to get irq\n"); 816 dev_err(dev, "Failed to get irq\n");
743 return state->irq; 817 return state->irq;
744 } 818 }
745 819
746 for (i = 0; i < CSIS_NUM_SUPPLIES; i++) 820 for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
747 state->supplies[i].supply = csis_supply_name[i]; 821 state->supplies[i].supply = csis_supply_name[i];
748 822
749 ret = devm_regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES, 823 ret = devm_regulator_bulk_get(dev, CSIS_NUM_SUPPLIES,
750 state->supplies); 824 state->supplies);
751 if (ret) 825 if (ret)
752 return ret; 826 return ret;
@@ -755,11 +829,11 @@ static int s5pcsis_probe(struct platform_device *pdev)
755 if (ret < 0) 829 if (ret < 0)
756 return ret; 830 return ret;
757 831
758 if (pdata->clk_rate) 832 if (state->clk_frequency)
759 ret = clk_set_rate(state->clock[CSIS_CLK_MUX], 833 ret = clk_set_rate(state->clock[CSIS_CLK_MUX],
760 pdata->clk_rate); 834 state->clk_frequency);
761 else 835 else
762 dev_WARN(&pdev->dev, "No clock frequency specified!\n"); 836 dev_WARN(dev, "No clock frequency specified!\n");
763 if (ret < 0) 837 if (ret < 0)
764 goto e_clkput; 838 goto e_clkput;
765 839
@@ -767,16 +841,17 @@ static int s5pcsis_probe(struct platform_device *pdev)
767 if (ret < 0) 841 if (ret < 0)
768 goto e_clkput; 842 goto e_clkput;
769 843
770 ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler, 844 ret = devm_request_irq(dev, state->irq, s5pcsis_irq_handler,
771 0, dev_name(&pdev->dev), state); 845 0, dev_name(dev), state);
772 if (ret) { 846 if (ret) {
773 dev_err(&pdev->dev, "Interrupt request failed\n"); 847 dev_err(dev, "Interrupt request failed\n");
774 goto e_clkdis; 848 goto e_clkdis;
775 } 849 }
776 850
777 v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops); 851 v4l2_subdev_init(&state->sd, &s5pcsis_subdev_ops);
778 state->sd.owner = THIS_MODULE; 852 state->sd.owner = THIS_MODULE;
779 strlcpy(state->sd.name, dev_name(&pdev->dev), sizeof(state->sd.name)); 853 snprintf(state->sd.name, sizeof(state->sd.name), "%s.%d",
854 CSIS_SUBDEV_NAME, state->index);
780 state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 855 state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
781 state->csis_fmt = &s5pcsis_formats[0]; 856 state->csis_fmt = &s5pcsis_formats[0];
782 857
@@ -796,10 +871,12 @@ static int s5pcsis_probe(struct platform_device *pdev)
796 871
797 /* .. and a pointer to the subdev. */ 872 /* .. and a pointer to the subdev. */
798 platform_set_drvdata(pdev, &state->sd); 873 platform_set_drvdata(pdev, &state->sd);
799
800 memcpy(state->events, s5pcsis_events, sizeof(state->events)); 874 memcpy(state->events, s5pcsis_events, sizeof(state->events));
875 pm_runtime_enable(dev);
801 876
802 pm_runtime_enable(&pdev->dev); 877 dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",
878 state->num_lanes, state->hs_settle, state->wclk_ext,
879 state->clk_frequency);
803 return 0; 880 return 0;
804 881
805e_clkdis: 882e_clkdis:
@@ -923,13 +1000,21 @@ static const struct dev_pm_ops s5pcsis_pm_ops = {
923 SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) 1000 SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume)
924}; 1001};
925 1002
1003static const struct of_device_id s5pcsis_of_match[] = {
1004 { .compatible = "samsung,s5pv210-csis" },
1005 { .compatible = "samsung,exynos4210-csis" },
1006 { /* sentinel */ },
1007};
1008MODULE_DEVICE_TABLE(of, s5pcsis_of_match);
1009
926static struct platform_driver s5pcsis_driver = { 1010static struct platform_driver s5pcsis_driver = {
927 .probe = s5pcsis_probe, 1011 .probe = s5pcsis_probe,
928 .remove = s5pcsis_remove, 1012 .remove = s5pcsis_remove,
929 .driver = { 1013 .driver = {
930 .name = CSIS_DRIVER_NAME, 1014 .of_match_table = s5pcsis_of_match,
931 .owner = THIS_MODULE, 1015 .name = CSIS_DRIVER_NAME,
932 .pm = &s5pcsis_pm_ops, 1016 .owner = THIS_MODULE,
1017 .pm = &s5pcsis_pm_ops,
933 }, 1018 },
934}; 1019};
935 1020
diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.h b/drivers/media/platform/s5p-fimc/mipi-csis.h
index 2709286396e1..28c11c4085d8 100644
--- a/drivers/media/platform/s5p-fimc/mipi-csis.h
+++ b/drivers/media/platform/s5p-fimc/mipi-csis.h
@@ -11,6 +11,7 @@
11#define S5P_MIPI_CSIS_H_ 11#define S5P_MIPI_CSIS_H_
12 12
13#define CSIS_DRIVER_NAME "s5p-mipi-csis" 13#define CSIS_DRIVER_NAME "s5p-mipi-csis"
14#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
14#define CSIS_MAX_ENTITIES 2 15#define CSIS_MAX_ENTITIES 2
15#define CSIS0_MAX_LANES 4 16#define CSIS0_MAX_LANES 4
16#define CSIS1_MAX_LANES 2 17#define CSIS1_MAX_LANES 2