diff options
author | Sylwester Nawrocki <s.nawrocki@samsung.com> | 2013-12-20 16:53:53 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-03-14 09:34:44 -0400 |
commit | d3f5e0c54f1bfa5f48e92ac45a279fa8cfdc55b7 (patch) | |
tree | 27badefa1ce44fa53126c86cfc8b2c9fcc08f3b0 /drivers/media/platform | |
parent | d265d9ac6c7c3201f0fea737cdf9c74e50415178 (diff) |
[media] exynos4-is: Add clock provider for the SCLK_CAM clock outputs
This patch adds clock provider so the the SCLK_CAM0/1 output clocks
can be accessed by image sensor devices through the clk API.
Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
Diffstat (limited to 'drivers/media/platform')
-rw-r--r-- | drivers/media/platform/exynos4-is/media-dev.c | 118 | ||||
-rw-r--r-- | drivers/media/platform/exynos4-is/media-dev.h | 19 |
2 files changed, 136 insertions, 1 deletions
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index c1bce170df6f..f047a9f1043c 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c | |||
@@ -11,6 +11,8 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/bug.h> | 13 | #include <linux/bug.h> |
14 | #include <linux/clk.h> | ||
15 | #include <linux/clk-provider.h> | ||
14 | #include <linux/device.h> | 16 | #include <linux/device.h> |
15 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
16 | #include <linux/i2c.h> | 18 | #include <linux/i2c.h> |
@@ -1276,6 +1278,14 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) | |||
1276 | struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); | 1278 | struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); |
1277 | struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); | 1279 | struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); |
1278 | 1280 | ||
1281 | /* | ||
1282 | * If there is a clock provider registered the sensors will | ||
1283 | * handle their clock themselves, no need to control it on | ||
1284 | * the host interface side. | ||
1285 | */ | ||
1286 | if (fmd->clk_provider.num_clocks > 0) | ||
1287 | return 0; | ||
1288 | |||
1279 | return __fimc_md_set_camclk(fmd, si, on); | 1289 | return __fimc_md_set_camclk(fmd, si, on); |
1280 | } | 1290 | } |
1281 | 1291 | ||
@@ -1437,6 +1447,103 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) | |||
1437 | return 0; | 1447 | return 0; |
1438 | } | 1448 | } |
1439 | 1449 | ||
1450 | #ifdef CONFIG_OF | ||
1451 | static int cam_clk_prepare(struct clk_hw *hw) | ||
1452 | { | ||
1453 | struct cam_clk *camclk = to_cam_clk(hw); | ||
1454 | int ret; | ||
1455 | |||
1456 | if (camclk->fmd->pmf == NULL) | ||
1457 | return -ENODEV; | ||
1458 | |||
1459 | ret = pm_runtime_get_sync(camclk->fmd->pmf); | ||
1460 | return ret < 0 ? ret : 0; | ||
1461 | } | ||
1462 | |||
1463 | static void cam_clk_unprepare(struct clk_hw *hw) | ||
1464 | { | ||
1465 | struct cam_clk *camclk = to_cam_clk(hw); | ||
1466 | |||
1467 | if (camclk->fmd->pmf == NULL) | ||
1468 | return; | ||
1469 | |||
1470 | pm_runtime_put_sync(camclk->fmd->pmf); | ||
1471 | } | ||
1472 | |||
1473 | static const struct clk_ops cam_clk_ops = { | ||
1474 | .prepare = cam_clk_prepare, | ||
1475 | .unprepare = cam_clk_unprepare, | ||
1476 | }; | ||
1477 | |||
1478 | static void fimc_md_unregister_clk_provider(struct fimc_md *fmd) | ||
1479 | { | ||
1480 | struct cam_clk_provider *cp = &fmd->clk_provider; | ||
1481 | unsigned int i; | ||
1482 | |||
1483 | if (cp->of_node) | ||
1484 | of_clk_del_provider(cp->of_node); | ||
1485 | |||
1486 | for (i = 0; i < cp->num_clocks; i++) | ||
1487 | clk_unregister(cp->clks[i]); | ||
1488 | } | ||
1489 | |||
1490 | static int fimc_md_register_clk_provider(struct fimc_md *fmd) | ||
1491 | { | ||
1492 | struct cam_clk_provider *cp = &fmd->clk_provider; | ||
1493 | struct device *dev = &fmd->pdev->dev; | ||
1494 | int i, ret; | ||
1495 | |||
1496 | for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { | ||
1497 | struct cam_clk *camclk = &cp->camclk[i]; | ||
1498 | struct clk_init_data init; | ||
1499 | const char *p_name; | ||
1500 | |||
1501 | ret = of_property_read_string_index(dev->of_node, | ||
1502 | "clock-output-names", i, &init.name); | ||
1503 | if (ret < 0) | ||
1504 | break; | ||
1505 | |||
1506 | p_name = __clk_get_name(fmd->camclk[i].clock); | ||
1507 | |||
1508 | /* It's safe since clk_register() will duplicate the string. */ | ||
1509 | init.parent_names = &p_name; | ||
1510 | init.num_parents = 1; | ||
1511 | init.ops = &cam_clk_ops; | ||
1512 | init.flags = CLK_SET_RATE_PARENT; | ||
1513 | camclk->hw.init = &init; | ||
1514 | camclk->fmd = fmd; | ||
1515 | |||
1516 | cp->clks[i] = clk_register(NULL, &camclk->hw); | ||
1517 | if (IS_ERR(cp->clks[i])) { | ||
1518 | dev_err(dev, "failed to register clock: %s (%ld)\n", | ||
1519 | init.name, PTR_ERR(cp->clks[i])); | ||
1520 | ret = PTR_ERR(cp->clks[i]); | ||
1521 | goto err; | ||
1522 | } | ||
1523 | cp->num_clocks++; | ||
1524 | } | ||
1525 | |||
1526 | if (cp->num_clocks == 0) { | ||
1527 | dev_warn(dev, "clk provider not registered\n"); | ||
1528 | return 0; | ||
1529 | } | ||
1530 | |||
1531 | cp->clk_data.clks = cp->clks; | ||
1532 | cp->clk_data.clk_num = cp->num_clocks; | ||
1533 | cp->of_node = dev->of_node; | ||
1534 | ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, | ||
1535 | &cp->clk_data); | ||
1536 | if (ret == 0) | ||
1537 | return 0; | ||
1538 | err: | ||
1539 | fimc_md_unregister_clk_provider(fmd); | ||
1540 | return ret; | ||
1541 | } | ||
1542 | #else | ||
1543 | #define fimc_md_register_clk_provider(fmd) (0) | ||
1544 | #define fimc_md_unregister_clk_provider(fmd) (0) | ||
1545 | #endif | ||
1546 | |||
1440 | static int fimc_md_probe(struct platform_device *pdev) | 1547 | static int fimc_md_probe(struct platform_device *pdev) |
1441 | { | 1548 | { |
1442 | struct device *dev = &pdev->dev; | 1549 | struct device *dev = &pdev->dev; |
@@ -1464,16 +1571,24 @@ static int fimc_md_probe(struct platform_device *pdev) | |||
1464 | 1571 | ||
1465 | fmd->use_isp = fimc_md_is_isp_available(dev->of_node); | 1572 | fmd->use_isp = fimc_md_is_isp_available(dev->of_node); |
1466 | 1573 | ||
1574 | ret = fimc_md_register_clk_provider(fmd); | ||
1575 | if (ret < 0) { | ||
1576 | v4l2_err(v4l2_dev, "clock provider registration failed\n"); | ||
1577 | return ret; | ||
1578 | } | ||
1579 | |||
1467 | ret = v4l2_device_register(dev, &fmd->v4l2_dev); | 1580 | ret = v4l2_device_register(dev, &fmd->v4l2_dev); |
1468 | if (ret < 0) { | 1581 | if (ret < 0) { |
1469 | v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); | 1582 | v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); |
1470 | return ret; | 1583 | return ret; |
1471 | } | 1584 | } |
1585 | |||
1472 | ret = media_device_register(&fmd->media_dev); | 1586 | ret = media_device_register(&fmd->media_dev); |
1473 | if (ret < 0) { | 1587 | if (ret < 0) { |
1474 | v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); | 1588 | v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); |
1475 | goto err_md; | 1589 | goto err_md; |
1476 | } | 1590 | } |
1591 | |||
1477 | ret = fimc_md_get_clocks(fmd); | 1592 | ret = fimc_md_get_clocks(fmd); |
1478 | if (ret) | 1593 | if (ret) |
1479 | goto err_clk; | 1594 | goto err_clk; |
@@ -1507,6 +1622,7 @@ static int fimc_md_probe(struct platform_device *pdev) | |||
1507 | ret = fimc_md_create_links(fmd); | 1622 | ret = fimc_md_create_links(fmd); |
1508 | if (ret) | 1623 | if (ret) |
1509 | goto err_unlock; | 1624 | goto err_unlock; |
1625 | |||
1510 | ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); | 1626 | ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); |
1511 | if (ret) | 1627 | if (ret) |
1512 | goto err_unlock; | 1628 | goto err_unlock; |
@@ -1527,6 +1643,7 @@ err_clk: | |||
1527 | media_device_unregister(&fmd->media_dev); | 1643 | media_device_unregister(&fmd->media_dev); |
1528 | err_md: | 1644 | err_md: |
1529 | v4l2_device_unregister(&fmd->v4l2_dev); | 1645 | v4l2_device_unregister(&fmd->v4l2_dev); |
1646 | fimc_md_unregister_clk_provider(fmd); | ||
1530 | return ret; | 1647 | return ret; |
1531 | } | 1648 | } |
1532 | 1649 | ||
@@ -1537,6 +1654,7 @@ static int fimc_md_remove(struct platform_device *pdev) | |||
1537 | if (!fmd) | 1654 | if (!fmd) |
1538 | return 0; | 1655 | return 0; |
1539 | 1656 | ||
1657 | fimc_md_unregister_clk_provider(fmd); | ||
1540 | v4l2_device_unregister(&fmd->v4l2_dev); | 1658 | v4l2_device_unregister(&fmd->v4l2_dev); |
1541 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); | 1659 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); |
1542 | fimc_md_unregister_entities(fmd); | 1660 | fimc_md_unregister_entities(fmd); |
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 62599fd7756f..a88cee59fd2f 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h | |||
@@ -10,6 +10,7 @@ | |||
10 | #define FIMC_MDEVICE_H_ | 10 | #define FIMC_MDEVICE_H_ |
11 | 11 | ||
12 | #include <linux/clk.h> | 12 | #include <linux/clk.h> |
13 | #include <linux/clk-provider.h> | ||
13 | #include <linux/platform_device.h> | 14 | #include <linux/platform_device.h> |
14 | #include <linux/mutex.h> | 15 | #include <linux/mutex.h> |
15 | #include <linux/of.h> | 16 | #include <linux/of.h> |
@@ -89,6 +90,12 @@ struct fimc_sensor_info { | |||
89 | struct fimc_dev *host; | 90 | struct fimc_dev *host; |
90 | }; | 91 | }; |
91 | 92 | ||
93 | struct cam_clk { | ||
94 | struct clk_hw hw; | ||
95 | struct fimc_md *fmd; | ||
96 | }; | ||
97 | #define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw) | ||
98 | |||
92 | /** | 99 | /** |
93 | * struct fimc_md - fimc media device information | 100 | * struct fimc_md - fimc media device information |
94 | * @csis: MIPI CSIS subdevs data | 101 | * @csis: MIPI CSIS subdevs data |
@@ -105,6 +112,7 @@ struct fimc_sensor_info { | |||
105 | * @pinctrl: camera port pinctrl handle | 112 | * @pinctrl: camera port pinctrl handle |
106 | * @state_default: pinctrl default state handle | 113 | * @state_default: pinctrl default state handle |
107 | * @state_idle: pinctrl idle state handle | 114 | * @state_idle: pinctrl idle state handle |
115 | * @cam_clk_provider: CAMCLK clock provider structure | ||
108 | * @user_subdev_api: true if subdevs are not configured by the host driver | 116 | * @user_subdev_api: true if subdevs are not configured by the host driver |
109 | * @slock: spinlock protecting @sensor array | 117 | * @slock: spinlock protecting @sensor array |
110 | */ | 118 | */ |
@@ -122,13 +130,22 @@ struct fimc_md { | |||
122 | struct media_device media_dev; | 130 | struct media_device media_dev; |
123 | struct v4l2_device v4l2_dev; | 131 | struct v4l2_device v4l2_dev; |
124 | struct platform_device *pdev; | 132 | struct platform_device *pdev; |
133 | |||
125 | struct fimc_pinctrl { | 134 | struct fimc_pinctrl { |
126 | struct pinctrl *pinctrl; | 135 | struct pinctrl *pinctrl; |
127 | struct pinctrl_state *state_default; | 136 | struct pinctrl_state *state_default; |
128 | struct pinctrl_state *state_idle; | 137 | struct pinctrl_state *state_idle; |
129 | } pinctl; | 138 | } pinctl; |
130 | bool user_subdev_api; | ||
131 | 139 | ||
140 | struct cam_clk_provider { | ||
141 | struct clk *clks[FIMC_MAX_CAMCLKS]; | ||
142 | struct clk_onecell_data clk_data; | ||
143 | struct device_node *of_node; | ||
144 | struct cam_clk camclk[FIMC_MAX_CAMCLKS]; | ||
145 | int num_clocks; | ||
146 | } clk_provider; | ||
147 | |||
148 | bool user_subdev_api; | ||
132 | spinlock_t slock; | 149 | spinlock_t slock; |
133 | struct list_head pipelines; | 150 | struct list_head pipelines; |
134 | }; | 151 | }; |