diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-05 16:10:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-05 16:10:00 -0400 |
commit | 463b21fb27509061b3e97fb4fa69f26d089ddaf4 (patch) | |
tree | 4d8fb4302e4edbfb5c4dfe7c6570a4c1b4c07756 /drivers/media | |
parent | 8e0c0832348c7fda1c85d67697cfe4adf077344c (diff) | |
parent | 97e9858ed5525af5355769cd98b25b5ec94c0c85 (diff) |
Merge branch 'topic/exynos' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull exynos media updates from Mauro Carvalho Chehab:
"These are the remaining patches I have for the merge windows. It
basically adds a new sensor and adds the needed DT bits for it to
work"
* 'topic/exynos' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media:
[media] s5p-fimc: Remove reference to outdated macro
[media] s5p-jpeg: Fix broken indentation in jpeg-regs.h
[media] exynos4-is: Add the FIMC-IS ISP capture DMA driver
[media] exynos4-is: Add support for asynchronous subdevices registration
[media] exynos4-is: Add clock provider for the SCLK_CAM clock outputs
[media] exynos4-is: Use external s5k6a3 sensor driver
[media] V4L: s5c73m3: Add device tree support
[media] V4L: Add driver for s5k6a3 image sensor
[media] Documentation: devicetree: Update Samsung FIMC DT binding
[media] Documentation: dt: Add binding documentation for S5C73M3 camera
[media] Documentation: dt: Add binding documentation for S5K6A3 image sensor
Diffstat (limited to 'drivers/media')
23 files changed, 1725 insertions, 547 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c930be30eb7e..441053be7f55 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig | |||
@@ -579,6 +579,14 @@ config VIDEO_S5K6AA | |||
579 | This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M | 579 | This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M |
580 | camera sensor with an embedded SoC image signal processor. | 580 | camera sensor with an embedded SoC image signal processor. |
581 | 581 | ||
582 | config VIDEO_S5K6A3 | ||
583 | tristate "Samsung S5K6A3 sensor support" | ||
584 | depends on MEDIA_CAMERA_SUPPORT | ||
585 | depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API | ||
586 | ---help--- | ||
587 | This is a V4L2 sensor-level driver for Samsung S5K6A3 raw | ||
588 | camera sensor. | ||
589 | |||
582 | config VIDEO_S5K4ECGX | 590 | config VIDEO_S5K4ECGX |
583 | tristate "Samsung S5K4ECGX sensor support" | 591 | tristate "Samsung S5K4ECGX sensor support" |
584 | depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API | 592 | depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API |
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 01b6bfc0db5b..01ae9328e582 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile | |||
@@ -66,6 +66,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o | |||
66 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o | 66 | obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o |
67 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o | 67 | obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o |
68 | obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o | 68 | obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o |
69 | obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o | ||
69 | obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o | 70 | obj-$(CONFIG_VIDEO_S5K4ECGX) += s5k4ecgx.o |
70 | obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o | 71 | obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o |
71 | obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ | 72 | obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ |
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index e7f555cc827a..a4459301b5f8 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c | |||
@@ -15,7 +15,7 @@ | |||
15 | * GNU General Public License for more details. | 15 | * GNU General Public License for more details. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/sizes.h> | 18 | #include <linux/clk.h> |
19 | #include <linux/delay.h> | 19 | #include <linux/delay.h> |
20 | #include <linux/firmware.h> | 20 | #include <linux/firmware.h> |
21 | #include <linux/gpio.h> | 21 | #include <linux/gpio.h> |
@@ -23,7 +23,9 @@ | |||
23 | #include <linux/init.h> | 23 | #include <linux/init.h> |
24 | #include <linux/media.h> | 24 | #include <linux/media.h> |
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/of_gpio.h> | ||
26 | #include <linux/regulator/consumer.h> | 27 | #include <linux/regulator/consumer.h> |
28 | #include <linux/sizes.h> | ||
27 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
28 | #include <linux/spi/spi.h> | 30 | #include <linux/spi/spi.h> |
29 | #include <linux/videodev2.h> | 31 | #include <linux/videodev2.h> |
@@ -33,6 +35,7 @@ | |||
33 | #include <media/v4l2-subdev.h> | 35 | #include <media/v4l2-subdev.h> |
34 | #include <media/v4l2-mediabus.h> | 36 | #include <media/v4l2-mediabus.h> |
35 | #include <media/s5c73m3.h> | 37 | #include <media/s5c73m3.h> |
38 | #include <media/v4l2-of.h> | ||
36 | 39 | ||
37 | #include "s5c73m3.h" | 40 | #include "s5c73m3.h" |
38 | 41 | ||
@@ -46,6 +49,8 @@ static int update_fw; | |||
46 | module_param(update_fw, int, 0644); | 49 | module_param(update_fw, int, 0644); |
47 | 50 | ||
48 | #define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K | 51 | #define S5C73M3_EMBEDDED_DATA_MAXLEN SZ_4K |
52 | #define S5C73M3_MIPI_DATA_LANES 4 | ||
53 | #define S5C73M3_CLK_NAME "cis_extclk" | ||
49 | 54 | ||
50 | static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = { | 55 | static const char * const s5c73m3_supply_names[S5C73M3_MAX_SUPPLIES] = { |
51 | "vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ | 56 | "vdd-int", /* Digital Core supply (1.2V), CAM_ISP_CORE_1.2V */ |
@@ -1355,9 +1360,20 @@ static int __s5c73m3_power_on(struct s5c73m3 *state) | |||
1355 | for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) { | 1360 | for (i = 0; i < S5C73M3_MAX_SUPPLIES; i++) { |
1356 | ret = regulator_enable(state->supplies[i].consumer); | 1361 | ret = regulator_enable(state->supplies[i].consumer); |
1357 | if (ret) | 1362 | if (ret) |
1358 | goto err; | 1363 | goto err_reg_dis; |
1359 | } | 1364 | } |
1360 | 1365 | ||
1366 | ret = clk_set_rate(state->clock, state->mclk_frequency); | ||
1367 | if (ret < 0) | ||
1368 | goto err_reg_dis; | ||
1369 | |||
1370 | ret = clk_prepare_enable(state->clock); | ||
1371 | if (ret < 0) | ||
1372 | goto err_reg_dis; | ||
1373 | |||
1374 | v4l2_dbg(1, s5c73m3_dbg, &state->oif_sd, "clock frequency: %ld\n", | ||
1375 | clk_get_rate(state->clock)); | ||
1376 | |||
1361 | s5c73m3_gpio_deassert(state, STBY); | 1377 | s5c73m3_gpio_deassert(state, STBY); |
1362 | usleep_range(100, 200); | 1378 | usleep_range(100, 200); |
1363 | 1379 | ||
@@ -1365,7 +1381,8 @@ static int __s5c73m3_power_on(struct s5c73m3 *state) | |||
1365 | usleep_range(50, 100); | 1381 | usleep_range(50, 100); |
1366 | 1382 | ||
1367 | return 0; | 1383 | return 0; |
1368 | err: | 1384 | |
1385 | err_reg_dis: | ||
1369 | for (--i; i >= 0; i--) | 1386 | for (--i; i >= 0; i--) |
1370 | regulator_disable(state->supplies[i].consumer); | 1387 | regulator_disable(state->supplies[i].consumer); |
1371 | return ret; | 1388 | return ret; |
@@ -1380,6 +1397,9 @@ static int __s5c73m3_power_off(struct s5c73m3 *state) | |||
1380 | 1397 | ||
1381 | if (s5c73m3_gpio_assert(state, STBY)) | 1398 | if (s5c73m3_gpio_assert(state, STBY)) |
1382 | usleep_range(100, 200); | 1399 | usleep_range(100, 200); |
1400 | |||
1401 | clk_disable_unprepare(state->clock); | ||
1402 | |||
1383 | state->streaming = 0; | 1403 | state->streaming = 0; |
1384 | state->isp_ready = 0; | 1404 | state->isp_ready = 0; |
1385 | 1405 | ||
@@ -1388,6 +1408,7 @@ static int __s5c73m3_power_off(struct s5c73m3 *state) | |||
1388 | if (ret) | 1408 | if (ret) |
1389 | goto err; | 1409 | goto err; |
1390 | } | 1410 | } |
1411 | |||
1391 | return 0; | 1412 | return 0; |
1392 | err: | 1413 | err: |
1393 | for (++i; i < S5C73M3_MAX_SUPPLIES; i++) { | 1414 | for (++i; i < S5C73M3_MAX_SUPPLIES; i++) { |
@@ -1396,6 +1417,8 @@ err: | |||
1396 | v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n", | 1417 | v4l2_err(&state->oif_sd, "Failed to reenable %s: %d\n", |
1397 | state->supplies[i].supply, r); | 1418 | state->supplies[i].supply, r); |
1398 | } | 1419 | } |
1420 | |||
1421 | clk_prepare_enable(state->clock); | ||
1399 | return ret; | 1422 | return ret; |
1400 | } | 1423 | } |
1401 | 1424 | ||
@@ -1451,17 +1474,6 @@ static int s5c73m3_oif_registered(struct v4l2_subdev *sd) | |||
1451 | S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, | 1474 | S5C73M3_JPEG_PAD, &state->oif_sd.entity, OIF_JPEG_PAD, |
1452 | MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); | 1475 | MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); |
1453 | 1476 | ||
1454 | mutex_lock(&state->lock); | ||
1455 | ret = __s5c73m3_power_on(state); | ||
1456 | if (ret == 0) | ||
1457 | s5c73m3_get_fw_version(state); | ||
1458 | |||
1459 | __s5c73m3_power_off(state); | ||
1460 | mutex_unlock(&state->lock); | ||
1461 | |||
1462 | v4l2_dbg(1, s5c73m3_dbg, sd, "%s: Booting %s (%d)\n", | ||
1463 | __func__, ret ? "failed" : "succeeded", ret); | ||
1464 | |||
1465 | return ret; | 1477 | return ret; |
1466 | } | 1478 | } |
1467 | 1479 | ||
@@ -1519,41 +1531,112 @@ static const struct v4l2_subdev_ops oif_subdev_ops = { | |||
1519 | .video = &s5c73m3_oif_video_ops, | 1531 | .video = &s5c73m3_oif_video_ops, |
1520 | }; | 1532 | }; |
1521 | 1533 | ||
1522 | static int s5c73m3_configure_gpios(struct s5c73m3 *state, | 1534 | static int s5c73m3_configure_gpios(struct s5c73m3 *state) |
1523 | const struct s5c73m3_platform_data *pdata) | 1535 | { |
1536 | static const char * const gpio_names[] = { | ||
1537 | "S5C73M3_STBY", "S5C73M3_RST" | ||
1538 | }; | ||
1539 | struct i2c_client *c = state->i2c_client; | ||
1540 | struct s5c73m3_gpio *g = state->gpio; | ||
1541 | int ret, i; | ||
1542 | |||
1543 | for (i = 0; i < GPIO_NUM; ++i) { | ||
1544 | unsigned int flags = GPIOF_DIR_OUT; | ||
1545 | if (g[i].level) | ||
1546 | flags |= GPIOF_INIT_HIGH; | ||
1547 | ret = devm_gpio_request_one(&c->dev, g[i].gpio, flags, | ||
1548 | gpio_names[i]); | ||
1549 | if (ret) { | ||
1550 | v4l2_err(c, "failed to request gpio %s\n", | ||
1551 | gpio_names[i]); | ||
1552 | return ret; | ||
1553 | } | ||
1554 | } | ||
1555 | return 0; | ||
1556 | } | ||
1557 | |||
1558 | static int s5c73m3_parse_gpios(struct s5c73m3 *state) | ||
1559 | { | ||
1560 | static const char * const prop_names[] = { | ||
1561 | "standby-gpios", "xshutdown-gpios", | ||
1562 | }; | ||
1563 | struct device *dev = &state->i2c_client->dev; | ||
1564 | struct device_node *node = dev->of_node; | ||
1565 | int ret, i; | ||
1566 | |||
1567 | for (i = 0; i < GPIO_NUM; ++i) { | ||
1568 | enum of_gpio_flags of_flags; | ||
1569 | |||
1570 | ret = of_get_named_gpio_flags(node, prop_names[i], | ||
1571 | 0, &of_flags); | ||
1572 | if (ret < 0) { | ||
1573 | dev_err(dev, "failed to parse %s DT property\n", | ||
1574 | prop_names[i]); | ||
1575 | return -EINVAL; | ||
1576 | } | ||
1577 | state->gpio[i].gpio = ret; | ||
1578 | state->gpio[i].level = !(of_flags & OF_GPIO_ACTIVE_LOW); | ||
1579 | } | ||
1580 | return 0; | ||
1581 | } | ||
1582 | |||
1583 | static int s5c73m3_get_platform_data(struct s5c73m3 *state) | ||
1524 | { | 1584 | { |
1525 | struct device *dev = &state->i2c_client->dev; | 1585 | struct device *dev = &state->i2c_client->dev; |
1526 | const struct s5c73m3_gpio *gpio; | 1586 | const struct s5c73m3_platform_data *pdata = dev->platform_data; |
1527 | unsigned long flags; | 1587 | struct device_node *node = dev->of_node; |
1588 | struct device_node *node_ep; | ||
1589 | struct v4l2_of_endpoint ep; | ||
1528 | int ret; | 1590 | int ret; |
1529 | 1591 | ||
1530 | state->gpio[STBY].gpio = -EINVAL; | 1592 | if (!node) { |
1531 | state->gpio[RST].gpio = -EINVAL; | 1593 | if (!pdata) { |
1594 | dev_err(dev, "Platform data not specified\n"); | ||
1595 | return -EINVAL; | ||
1596 | } | ||
1532 | 1597 | ||
1533 | gpio = &pdata->gpio_stby; | 1598 | state->mclk_frequency = pdata->mclk_frequency; |
1534 | if (gpio_is_valid(gpio->gpio)) { | 1599 | state->gpio[STBY] = pdata->gpio_stby; |
1535 | flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) | 1600 | state->gpio[RST] = pdata->gpio_reset; |
1536 | | GPIOF_EXPORT; | 1601 | return 0; |
1537 | ret = devm_gpio_request_one(dev, gpio->gpio, flags, | 1602 | } |
1538 | "S5C73M3_STBY"); | 1603 | |
1539 | if (ret < 0) | 1604 | state->clock = devm_clk_get(dev, S5C73M3_CLK_NAME); |
1540 | return ret; | 1605 | if (IS_ERR(state->clock)) |
1606 | return PTR_ERR(state->clock); | ||
1541 | 1607 | ||
1542 | state->gpio[STBY] = *gpio; | 1608 | if (of_property_read_u32(node, "clock-frequency", |
1609 | &state->mclk_frequency)) { | ||
1610 | state->mclk_frequency = S5C73M3_DEFAULT_MCLK_FREQ; | ||
1611 | dev_info(dev, "using default %u Hz clock frequency\n", | ||
1612 | state->mclk_frequency); | ||
1543 | } | 1613 | } |
1544 | 1614 | ||
1545 | gpio = &pdata->gpio_reset; | 1615 | ret = s5c73m3_parse_gpios(state); |
1546 | if (gpio_is_valid(gpio->gpio)) { | 1616 | if (ret < 0) |
1547 | flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) | 1617 | return -EINVAL; |
1548 | | GPIOF_EXPORT; | ||
1549 | ret = devm_gpio_request_one(dev, gpio->gpio, flags, | ||
1550 | "S5C73M3_RST"); | ||
1551 | if (ret < 0) | ||
1552 | return ret; | ||
1553 | 1618 | ||
1554 | state->gpio[RST] = *gpio; | 1619 | node_ep = v4l2_of_get_next_endpoint(node, NULL); |
1620 | if (!node_ep) { | ||
1621 | dev_warn(dev, "no endpoint defined for node: %s\n", | ||
1622 | node->full_name); | ||
1623 | return 0; | ||
1555 | } | 1624 | } |
1556 | 1625 | ||
1626 | v4l2_of_parse_endpoint(node_ep, &ep); | ||
1627 | of_node_put(node_ep); | ||
1628 | |||
1629 | if (ep.bus_type != V4L2_MBUS_CSI2) { | ||
1630 | dev_err(dev, "unsupported bus type\n"); | ||
1631 | return -EINVAL; | ||
1632 | } | ||
1633 | /* | ||
1634 | * Number of MIPI CSI-2 data lanes is currently not configurable, | ||
1635 | * always a default value of 4 lanes is used. | ||
1636 | */ | ||
1637 | if (ep.bus.mipi_csi2.num_data_lanes != S5C73M3_MIPI_DATA_LANES) | ||
1638 | dev_info(dev, "falling back to 4 MIPI CSI-2 data lanes\n"); | ||
1639 | |||
1557 | return 0; | 1640 | return 0; |
1558 | } | 1641 | } |
1559 | 1642 | ||
@@ -1561,21 +1644,20 @@ static int s5c73m3_probe(struct i2c_client *client, | |||
1561 | const struct i2c_device_id *id) | 1644 | const struct i2c_device_id *id) |
1562 | { | 1645 | { |
1563 | struct device *dev = &client->dev; | 1646 | struct device *dev = &client->dev; |
1564 | const struct s5c73m3_platform_data *pdata = client->dev.platform_data; | ||
1565 | struct v4l2_subdev *sd; | 1647 | struct v4l2_subdev *sd; |
1566 | struct v4l2_subdev *oif_sd; | 1648 | struct v4l2_subdev *oif_sd; |
1567 | struct s5c73m3 *state; | 1649 | struct s5c73m3 *state; |
1568 | int ret, i; | 1650 | int ret, i; |
1569 | 1651 | ||
1570 | if (pdata == NULL) { | ||
1571 | dev_err(&client->dev, "Platform data not specified\n"); | ||
1572 | return -EINVAL; | ||
1573 | } | ||
1574 | |||
1575 | state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); | 1652 | state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); |
1576 | if (!state) | 1653 | if (!state) |
1577 | return -ENOMEM; | 1654 | return -ENOMEM; |
1578 | 1655 | ||
1656 | state->i2c_client = client; | ||
1657 | ret = s5c73m3_get_platform_data(state); | ||
1658 | if (ret < 0) | ||
1659 | return ret; | ||
1660 | |||
1579 | mutex_init(&state->lock); | 1661 | mutex_init(&state->lock); |
1580 | sd = &state->sensor_sd; | 1662 | sd = &state->sensor_sd; |
1581 | oif_sd = &state->oif_sd; | 1663 | oif_sd = &state->oif_sd; |
@@ -1613,11 +1695,7 @@ static int s5c73m3_probe(struct i2c_client *client, | |||
1613 | if (ret < 0) | 1695 | if (ret < 0) |
1614 | return ret; | 1696 | return ret; |
1615 | 1697 | ||
1616 | state->mclk_frequency = pdata->mclk_frequency; | 1698 | ret = s5c73m3_configure_gpios(state); |
1617 | state->bus_type = pdata->bus_type; | ||
1618 | state->i2c_client = client; | ||
1619 | |||
1620 | ret = s5c73m3_configure_gpios(state, pdata); | ||
1621 | if (ret) | 1699 | if (ret) |
1622 | goto out_err; | 1700 | goto out_err; |
1623 | 1701 | ||
@@ -1651,9 +1729,29 @@ static int s5c73m3_probe(struct i2c_client *client, | |||
1651 | if (ret < 0) | 1729 | if (ret < 0) |
1652 | goto out_err; | 1730 | goto out_err; |
1653 | 1731 | ||
1732 | oif_sd->dev = dev; | ||
1733 | |||
1734 | ret = __s5c73m3_power_on(state); | ||
1735 | if (ret < 0) | ||
1736 | goto out_err1; | ||
1737 | |||
1738 | ret = s5c73m3_get_fw_version(state); | ||
1739 | __s5c73m3_power_off(state); | ||
1740 | |||
1741 | if (ret < 0) { | ||
1742 | dev_err(dev, "Device detection failed: %d\n", ret); | ||
1743 | goto out_err1; | ||
1744 | } | ||
1745 | |||
1746 | ret = v4l2_async_register_subdev(oif_sd); | ||
1747 | if (ret < 0) | ||
1748 | goto out_err1; | ||
1749 | |||
1654 | v4l2_info(sd, "%s: completed successfully\n", __func__); | 1750 | v4l2_info(sd, "%s: completed successfully\n", __func__); |
1655 | return 0; | 1751 | return 0; |
1656 | 1752 | ||
1753 | out_err1: | ||
1754 | s5c73m3_unregister_spi_driver(state); | ||
1657 | out_err: | 1755 | out_err: |
1658 | media_entity_cleanup(&sd->entity); | 1756 | media_entity_cleanup(&sd->entity); |
1659 | return ret; | 1757 | return ret; |
@@ -1665,7 +1763,7 @@ static int s5c73m3_remove(struct i2c_client *client) | |||
1665 | struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd); | 1763 | struct s5c73m3 *state = oif_sd_to_s5c73m3(oif_sd); |
1666 | struct v4l2_subdev *sensor_sd = &state->sensor_sd; | 1764 | struct v4l2_subdev *sensor_sd = &state->sensor_sd; |
1667 | 1765 | ||
1668 | v4l2_device_unregister_subdev(oif_sd); | 1766 | v4l2_async_unregister_subdev(oif_sd); |
1669 | 1767 | ||
1670 | v4l2_ctrl_handler_free(oif_sd->ctrl_handler); | 1768 | v4l2_ctrl_handler_free(oif_sd->ctrl_handler); |
1671 | media_entity_cleanup(&oif_sd->entity); | 1769 | media_entity_cleanup(&oif_sd->entity); |
@@ -1684,8 +1782,17 @@ static const struct i2c_device_id s5c73m3_id[] = { | |||
1684 | }; | 1782 | }; |
1685 | MODULE_DEVICE_TABLE(i2c, s5c73m3_id); | 1783 | MODULE_DEVICE_TABLE(i2c, s5c73m3_id); |
1686 | 1784 | ||
1785 | #ifdef CONFIG_OF | ||
1786 | static const struct of_device_id s5c73m3_of_match[] = { | ||
1787 | { .compatible = "samsung,s5c73m3" }, | ||
1788 | { } | ||
1789 | }; | ||
1790 | MODULE_DEVICE_TABLE(of, s5c73m3_of_match); | ||
1791 | #endif | ||
1792 | |||
1687 | static struct i2c_driver s5c73m3_i2c_driver = { | 1793 | static struct i2c_driver s5c73m3_i2c_driver = { |
1688 | .driver = { | 1794 | .driver = { |
1795 | .of_match_table = of_match_ptr(s5c73m3_of_match), | ||
1689 | .name = DRIVER_NAME, | 1796 | .name = DRIVER_NAME, |
1690 | }, | 1797 | }, |
1691 | .probe = s5c73m3_probe, | 1798 | .probe = s5c73m3_probe, |
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c index 8079e26eb5e2..f60b265b4da1 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-spi.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-spi.c | |||
@@ -27,6 +27,11 @@ | |||
27 | 27 | ||
28 | #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" | 28 | #define S5C73M3_SPI_DRV_NAME "S5C73M3-SPI" |
29 | 29 | ||
30 | static const struct of_device_id s5c73m3_spi_ids[] = { | ||
31 | { .compatible = "samsung,s5c73m3" }, | ||
32 | { } | ||
33 | }; | ||
34 | |||
30 | enum spi_direction { | 35 | enum spi_direction { |
31 | SPI_DIR_RX, | 36 | SPI_DIR_RX, |
32 | SPI_DIR_TX | 37 | SPI_DIR_TX |
@@ -146,6 +151,7 @@ int s5c73m3_register_spi_driver(struct s5c73m3 *state) | |||
146 | spidrv->driver.name = S5C73M3_SPI_DRV_NAME; | 151 | spidrv->driver.name = S5C73M3_SPI_DRV_NAME; |
147 | spidrv->driver.bus = &spi_bus_type; | 152 | spidrv->driver.bus = &spi_bus_type; |
148 | spidrv->driver.owner = THIS_MODULE; | 153 | spidrv->driver.owner = THIS_MODULE; |
154 | spidrv->driver.of_match_table = s5c73m3_spi_ids; | ||
149 | 155 | ||
150 | return spi_register_driver(spidrv); | 156 | return spi_register_driver(spidrv); |
151 | } | 157 | } |
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3.h b/drivers/media/i2c/s5c73m3/s5c73m3.h index 9dfa516f6944..9656b6723dc6 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3.h +++ b/drivers/media/i2c/s5c73m3/s5c73m3.h | |||
@@ -17,6 +17,7 @@ | |||
17 | #ifndef S5C73M3_H_ | 17 | #ifndef S5C73M3_H_ |
18 | #define S5C73M3_H_ | 18 | #define S5C73M3_H_ |
19 | 19 | ||
20 | #include <linux/clk.h> | ||
20 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
21 | #include <linux/regulator/consumer.h> | 22 | #include <linux/regulator/consumer.h> |
22 | #include <media/v4l2-common.h> | 23 | #include <media/v4l2-common.h> |
@@ -321,6 +322,7 @@ enum s5c73m3_oif_pads { | |||
321 | 322 | ||
322 | 323 | ||
323 | #define S5C73M3_MAX_SUPPLIES 6 | 324 | #define S5C73M3_MAX_SUPPLIES 6 |
325 | #define S5C73M3_DEFAULT_MCLK_FREQ 24000000U | ||
324 | 326 | ||
325 | struct s5c73m3_ctrls { | 327 | struct s5c73m3_ctrls { |
326 | struct v4l2_ctrl_handler handler; | 328 | struct v4l2_ctrl_handler handler; |
@@ -391,6 +393,8 @@ struct s5c73m3 { | |||
391 | struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES]; | 393 | struct regulator_bulk_data supplies[S5C73M3_MAX_SUPPLIES]; |
392 | struct s5c73m3_gpio gpio[GPIO_NUM]; | 394 | struct s5c73m3_gpio gpio[GPIO_NUM]; |
393 | 395 | ||
396 | struct clk *clock; | ||
397 | |||
394 | /* External master clock frequency */ | 398 | /* External master clock frequency */ |
395 | u32 mclk_frequency; | 399 | u32 mclk_frequency; |
396 | /* Video bus type - MIPI-CSI2/parallel */ | 400 | /* Video bus type - MIPI-CSI2/parallel */ |
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c new file mode 100644 index 000000000000..7bc2271ca009 --- /dev/null +++ b/drivers/media/i2c/s5k6a3.c | |||
@@ -0,0 +1,389 @@ | |||
1 | /* | ||
2 | * Samsung S5K6A3 image sensor driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/clk.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/gpio.h> | ||
17 | #include <linux/i2c.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/of_gpio.h> | ||
21 | #include <linux/pm_runtime.h> | ||
22 | #include <linux/regulator/consumer.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/videodev2.h> | ||
25 | #include <media/v4l2-async.h> | ||
26 | #include <media/v4l2-subdev.h> | ||
27 | |||
28 | #define S5K6A3_SENSOR_MAX_WIDTH 1412 | ||
29 | #define S5K6A3_SENSOR_MAX_HEIGHT 1412 | ||
30 | #define S5K6A3_SENSOR_MIN_WIDTH 32 | ||
31 | #define S5K6A3_SENSOR_MIN_HEIGHT 32 | ||
32 | |||
33 | #define S5K6A3_DEFAULT_WIDTH 1296 | ||
34 | #define S5K6A3_DEFAULT_HEIGHT 732 | ||
35 | |||
36 | #define S5K6A3_DRV_NAME "S5K6A3" | ||
37 | #define S5K6A3_CLK_NAME "extclk" | ||
38 | #define S5K6A3_DEFAULT_CLK_FREQ 24000000U | ||
39 | |||
40 | enum { | ||
41 | S5K6A3_SUPP_VDDA, | ||
42 | S5K6A3_SUPP_VDDIO, | ||
43 | S5K6A3_SUPP_AFVDD, | ||
44 | S5K6A3_NUM_SUPPLIES, | ||
45 | }; | ||
46 | |||
47 | /** | ||
48 | * struct s5k6a3 - fimc-is sensor data structure | ||
49 | * @dev: pointer to this I2C client device structure | ||
50 | * @subdev: the image sensor's v4l2 subdev | ||
51 | * @pad: subdev media source pad | ||
52 | * @supplies: image sensor's voltage regulator supplies | ||
53 | * @gpio_reset: GPIO connected to the sensor's reset pin | ||
54 | * @lock: mutex protecting the structure's members below | ||
55 | * @format: media bus format at the sensor's source pad | ||
56 | */ | ||
57 | struct s5k6a3 { | ||
58 | struct device *dev; | ||
59 | struct v4l2_subdev subdev; | ||
60 | struct media_pad pad; | ||
61 | struct regulator_bulk_data supplies[S5K6A3_NUM_SUPPLIES]; | ||
62 | int gpio_reset; | ||
63 | struct mutex lock; | ||
64 | struct v4l2_mbus_framefmt format; | ||
65 | struct clk *clock; | ||
66 | u32 clock_frequency; | ||
67 | int power_count; | ||
68 | }; | ||
69 | |||
70 | static const char * const s5k6a3_supply_names[] = { | ||
71 | [S5K6A3_SUPP_VDDA] = "svdda", | ||
72 | [S5K6A3_SUPP_VDDIO] = "svddio", | ||
73 | [S5K6A3_SUPP_AFVDD] = "afvdd", | ||
74 | }; | ||
75 | |||
76 | static inline struct s5k6a3 *sd_to_s5k6a3(struct v4l2_subdev *sd) | ||
77 | { | ||
78 | return container_of(sd, struct s5k6a3, subdev); | ||
79 | } | ||
80 | |||
81 | static const struct v4l2_mbus_framefmt s5k6a3_formats[] = { | ||
82 | { | ||
83 | .code = V4L2_MBUS_FMT_SGRBG10_1X10, | ||
84 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
85 | .field = V4L2_FIELD_NONE, | ||
86 | } | ||
87 | }; | ||
88 | |||
89 | static const struct v4l2_mbus_framefmt *find_sensor_format( | ||
90 | struct v4l2_mbus_framefmt *mf) | ||
91 | { | ||
92 | int i; | ||
93 | |||
94 | for (i = 0; i < ARRAY_SIZE(s5k6a3_formats); i++) | ||
95 | if (mf->code == s5k6a3_formats[i].code) | ||
96 | return &s5k6a3_formats[i]; | ||
97 | |||
98 | return &s5k6a3_formats[0]; | ||
99 | } | ||
100 | |||
101 | static int s5k6a3_enum_mbus_code(struct v4l2_subdev *sd, | ||
102 | struct v4l2_subdev_fh *fh, | ||
103 | struct v4l2_subdev_mbus_code_enum *code) | ||
104 | { | ||
105 | if (code->index >= ARRAY_SIZE(s5k6a3_formats)) | ||
106 | return -EINVAL; | ||
107 | |||
108 | code->code = s5k6a3_formats[code->index].code; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static void s5k6a3_try_format(struct v4l2_mbus_framefmt *mf) | ||
113 | { | ||
114 | const struct v4l2_mbus_framefmt *fmt; | ||
115 | |||
116 | fmt = find_sensor_format(mf); | ||
117 | mf->code = fmt->code; | ||
118 | v4l_bound_align_image(&mf->width, S5K6A3_SENSOR_MIN_WIDTH, | ||
119 | S5K6A3_SENSOR_MAX_WIDTH, 0, | ||
120 | &mf->height, S5K6A3_SENSOR_MIN_HEIGHT, | ||
121 | S5K6A3_SENSOR_MAX_HEIGHT, 0, 0); | ||
122 | } | ||
123 | |||
124 | static struct v4l2_mbus_framefmt *__s5k6a3_get_format( | ||
125 | struct s5k6a3 *sensor, struct v4l2_subdev_fh *fh, | ||
126 | u32 pad, enum v4l2_subdev_format_whence which) | ||
127 | { | ||
128 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
129 | return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; | ||
130 | |||
131 | return &sensor->format; | ||
132 | } | ||
133 | |||
134 | static int s5k6a3_set_fmt(struct v4l2_subdev *sd, | ||
135 | struct v4l2_subdev_fh *fh, | ||
136 | struct v4l2_subdev_format *fmt) | ||
137 | { | ||
138 | struct s5k6a3 *sensor = sd_to_s5k6a3(sd); | ||
139 | struct v4l2_mbus_framefmt *mf; | ||
140 | |||
141 | s5k6a3_try_format(&fmt->format); | ||
142 | |||
143 | mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which); | ||
144 | if (mf) { | ||
145 | mutex_lock(&sensor->lock); | ||
146 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) | ||
147 | *mf = fmt->format; | ||
148 | mutex_unlock(&sensor->lock); | ||
149 | } | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int s5k6a3_get_fmt(struct v4l2_subdev *sd, | ||
154 | struct v4l2_subdev_fh *fh, | ||
155 | struct v4l2_subdev_format *fmt) | ||
156 | { | ||
157 | struct s5k6a3 *sensor = sd_to_s5k6a3(sd); | ||
158 | struct v4l2_mbus_framefmt *mf; | ||
159 | |||
160 | mf = __s5k6a3_get_format(sensor, fh, fmt->pad, fmt->which); | ||
161 | |||
162 | mutex_lock(&sensor->lock); | ||
163 | fmt->format = *mf; | ||
164 | mutex_unlock(&sensor->lock); | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | static struct v4l2_subdev_pad_ops s5k6a3_pad_ops = { | ||
169 | .enum_mbus_code = s5k6a3_enum_mbus_code, | ||
170 | .get_fmt = s5k6a3_get_fmt, | ||
171 | .set_fmt = s5k6a3_set_fmt, | ||
172 | }; | ||
173 | |||
174 | static int s5k6a3_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
175 | { | ||
176 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); | ||
177 | |||
178 | *format = s5k6a3_formats[0]; | ||
179 | format->width = S5K6A3_DEFAULT_WIDTH; | ||
180 | format->height = S5K6A3_DEFAULT_HEIGHT; | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static const struct v4l2_subdev_internal_ops s5k6a3_sd_internal_ops = { | ||
186 | .open = s5k6a3_open, | ||
187 | }; | ||
188 | |||
189 | static int __s5k6a3_power_on(struct s5k6a3 *sensor) | ||
190 | { | ||
191 | int i = S5K6A3_SUPP_VDDA; | ||
192 | int ret; | ||
193 | |||
194 | ret = clk_set_rate(sensor->clock, sensor->clock_frequency); | ||
195 | if (ret < 0) | ||
196 | return ret; | ||
197 | |||
198 | ret = pm_runtime_get(sensor->dev); | ||
199 | if (ret < 0) | ||
200 | return ret; | ||
201 | |||
202 | ret = regulator_enable(sensor->supplies[i].consumer); | ||
203 | if (ret < 0) | ||
204 | goto error_rpm_put; | ||
205 | |||
206 | ret = clk_prepare_enable(sensor->clock); | ||
207 | if (ret < 0) | ||
208 | goto error_reg_dis; | ||
209 | |||
210 | for (i++; i < S5K6A3_NUM_SUPPLIES; i++) { | ||
211 | ret = regulator_enable(sensor->supplies[i].consumer); | ||
212 | if (ret < 0) | ||
213 | goto error_reg_dis; | ||
214 | } | ||
215 | |||
216 | gpio_set_value(sensor->gpio_reset, 1); | ||
217 | usleep_range(600, 800); | ||
218 | gpio_set_value(sensor->gpio_reset, 0); | ||
219 | usleep_range(600, 800); | ||
220 | gpio_set_value(sensor->gpio_reset, 1); | ||
221 | |||
222 | /* Delay needed for the sensor initialization */ | ||
223 | msleep(20); | ||
224 | return 0; | ||
225 | |||
226 | error_reg_dis: | ||
227 | for (--i; i >= 0; --i) | ||
228 | regulator_disable(sensor->supplies[i].consumer); | ||
229 | error_rpm_put: | ||
230 | pm_runtime_put(sensor->dev); | ||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static int __s5k6a3_power_off(struct s5k6a3 *sensor) | ||
235 | { | ||
236 | int i; | ||
237 | |||
238 | gpio_set_value(sensor->gpio_reset, 0); | ||
239 | |||
240 | for (i = S5K6A3_NUM_SUPPLIES - 1; i >= 0; i--) | ||
241 | regulator_disable(sensor->supplies[i].consumer); | ||
242 | |||
243 | clk_disable_unprepare(sensor->clock); | ||
244 | pm_runtime_put(sensor->dev); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static int s5k6a3_s_power(struct v4l2_subdev *sd, int on) | ||
249 | { | ||
250 | struct s5k6a3 *sensor = sd_to_s5k6a3(sd); | ||
251 | int ret = 0; | ||
252 | |||
253 | mutex_lock(&sensor->lock); | ||
254 | |||
255 | if (sensor->power_count == !on) { | ||
256 | if (on) | ||
257 | ret = __s5k6a3_power_on(sensor); | ||
258 | else | ||
259 | ret = __s5k6a3_power_off(sensor); | ||
260 | |||
261 | if (ret == 0) | ||
262 | sensor->power_count += on ? 1 : -1; | ||
263 | } | ||
264 | |||
265 | mutex_unlock(&sensor->lock); | ||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | static struct v4l2_subdev_core_ops s5k6a3_core_ops = { | ||
270 | .s_power = s5k6a3_s_power, | ||
271 | }; | ||
272 | |||
273 | static struct v4l2_subdev_ops s5k6a3_subdev_ops = { | ||
274 | .core = &s5k6a3_core_ops, | ||
275 | .pad = &s5k6a3_pad_ops, | ||
276 | }; | ||
277 | |||
278 | static int s5k6a3_probe(struct i2c_client *client, | ||
279 | const struct i2c_device_id *id) | ||
280 | { | ||
281 | struct device *dev = &client->dev; | ||
282 | struct s5k6a3 *sensor; | ||
283 | struct v4l2_subdev *sd; | ||
284 | int gpio, i, ret; | ||
285 | |||
286 | sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); | ||
287 | if (!sensor) | ||
288 | return -ENOMEM; | ||
289 | |||
290 | mutex_init(&sensor->lock); | ||
291 | sensor->gpio_reset = -EINVAL; | ||
292 | sensor->clock = ERR_PTR(-EINVAL); | ||
293 | sensor->dev = dev; | ||
294 | |||
295 | sensor->clock = devm_clk_get(sensor->dev, S5K6A3_CLK_NAME); | ||
296 | if (IS_ERR(sensor->clock)) | ||
297 | return PTR_ERR(sensor->clock); | ||
298 | |||
299 | gpio = of_get_gpio_flags(dev->of_node, 0, NULL); | ||
300 | if (!gpio_is_valid(gpio)) | ||
301 | return gpio; | ||
302 | |||
303 | ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, | ||
304 | S5K6A3_DRV_NAME); | ||
305 | if (ret < 0) | ||
306 | return ret; | ||
307 | |||
308 | sensor->gpio_reset = gpio; | ||
309 | |||
310 | if (of_property_read_u32(dev->of_node, "clock-frequency", | ||
311 | &sensor->clock_frequency)) { | ||
312 | sensor->clock_frequency = S5K6A3_DEFAULT_CLK_FREQ; | ||
313 | dev_info(dev, "using default %u Hz clock frequency\n", | ||
314 | sensor->clock_frequency); | ||
315 | } | ||
316 | |||
317 | for (i = 0; i < S5K6A3_NUM_SUPPLIES; i++) | ||
318 | sensor->supplies[i].supply = s5k6a3_supply_names[i]; | ||
319 | |||
320 | ret = devm_regulator_bulk_get(&client->dev, S5K6A3_NUM_SUPPLIES, | ||
321 | sensor->supplies); | ||
322 | if (ret < 0) | ||
323 | return ret; | ||
324 | |||
325 | sd = &sensor->subdev; | ||
326 | v4l2_i2c_subdev_init(sd, client, &s5k6a3_subdev_ops); | ||
327 | sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
328 | sd->internal_ops = &s5k6a3_sd_internal_ops; | ||
329 | |||
330 | sensor->format.code = s5k6a3_formats[0].code; | ||
331 | sensor->format.width = S5K6A3_DEFAULT_WIDTH; | ||
332 | sensor->format.height = S5K6A3_DEFAULT_HEIGHT; | ||
333 | |||
334 | sensor->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
335 | ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); | ||
336 | if (ret < 0) | ||
337 | return ret; | ||
338 | |||
339 | pm_runtime_no_callbacks(dev); | ||
340 | pm_runtime_enable(dev); | ||
341 | |||
342 | ret = v4l2_async_register_subdev(sd); | ||
343 | |||
344 | if (ret < 0) { | ||
345 | pm_runtime_disable(&client->dev); | ||
346 | media_entity_cleanup(&sd->entity); | ||
347 | } | ||
348 | |||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | static int s5k6a3_remove(struct i2c_client *client) | ||
353 | { | ||
354 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
355 | |||
356 | pm_runtime_disable(&client->dev); | ||
357 | v4l2_async_unregister_subdev(sd); | ||
358 | media_entity_cleanup(&sd->entity); | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static const struct i2c_device_id s5k6a3_ids[] = { | ||
363 | { } | ||
364 | }; | ||
365 | |||
366 | #ifdef CONFIG_OF | ||
367 | static const struct of_device_id s5k6a3_of_match[] = { | ||
368 | { .compatible = "samsung,s5k6a3" }, | ||
369 | { /* sentinel */ } | ||
370 | }; | ||
371 | MODULE_DEVICE_TABLE(of, s5k6a3_of_match); | ||
372 | #endif | ||
373 | |||
374 | static struct i2c_driver s5k6a3_driver = { | ||
375 | .driver = { | ||
376 | .of_match_table = of_match_ptr(s5k6a3_of_match), | ||
377 | .name = S5K6A3_DRV_NAME, | ||
378 | .owner = THIS_MODULE, | ||
379 | }, | ||
380 | .probe = s5k6a3_probe, | ||
381 | .remove = s5k6a3_remove, | ||
382 | .id_table = s5k6a3_ids, | ||
383 | }; | ||
384 | |||
385 | module_i2c_driver(s5k6a3_driver); | ||
386 | |||
387 | MODULE_DESCRIPTION("S5K6A3 image sensor subdev driver"); | ||
388 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
389 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 01ed1ecdff7e..e1b2ceba00c1 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig | |||
@@ -64,4 +64,13 @@ config VIDEO_EXYNOS4_FIMC_IS | |||
64 | To compile this driver as a module, choose M here: the | 64 | To compile this driver as a module, choose M here: the |
65 | module will be called exynos4-fimc-is. | 65 | module will be called exynos4-fimc-is. |
66 | 66 | ||
67 | config VIDEO_EXYNOS4_ISP_DMA_CAPTURE | ||
68 | bool "EXYNOS4x12 FIMC-IS ISP Direct DMA capture support" | ||
69 | depends on VIDEO_EXYNOS4_FIMC_IS | ||
70 | select VIDEO_EXYNOS4_IS_COMMON | ||
71 | default y | ||
72 | help | ||
73 | This option enables an additional video device node exposing a V4L2 | ||
74 | video capture interface for the FIMC-IS ISP raw (Bayer) capture DMA. | ||
75 | |||
67 | endif # VIDEO_SAMSUNG_EXYNOS4_IS | 76 | endif # VIDEO_SAMSUNG_EXYNOS4_IS |
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile index c2ff29ba6856..eed1b185d813 100644 --- a/drivers/media/platform/exynos4-is/Makefile +++ b/drivers/media/platform/exynos4-is/Makefile | |||
@@ -6,6 +6,10 @@ exynos4-is-common-objs := common.o | |||
6 | exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o | 6 | exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o |
7 | exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o | 7 | exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o |
8 | 8 | ||
9 | ifeq ($(CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE),y) | ||
10 | exynos-fimc-is-objs += fimc-isp-video.o | ||
11 | endif | ||
12 | |||
9 | obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o | 13 | obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o |
10 | obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o | 14 | obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o |
11 | obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o | 15 | obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o |
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c index 9bf3ddd9e028..bf1465d1bf6d 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c | |||
@@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is) | |||
56 | __hw_param_copy(dst, src); | 56 | __hw_param_copy(dst, src); |
57 | } | 57 | } |
58 | 58 | ||
59 | static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) | 59 | int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset) |
60 | { | 60 | { |
61 | struct is_param_region *par = &is->is_p_region->parameter; | 61 | struct is_param_region *par = &is->is_p_region->parameter; |
62 | struct chain_config *cfg = &is->config[is->config_index]; | 62 | struct chain_config *cfg = &is->config[is->config_index]; |
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/exynos4-is/fimc-is-param.h index f9358c27ae2d..8e31f7642776 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.h +++ b/drivers/media/platform/exynos4-is/fimc-is-param.h | |||
@@ -911,6 +911,10 @@ struct is_region { | |||
911 | u32 shared[MAX_SHARED_COUNT]; | 911 | u32 shared[MAX_SHARED_COUNT]; |
912 | } __packed; | 912 | } __packed; |
913 | 913 | ||
914 | /* Offset to the ISP DMA2 output buffer address array. */ | ||
915 | #define DMA2_OUTPUT_ADDR_ARRAY_OFFS \ | ||
916 | (offsetof(struct is_region, shared) + 32 * sizeof(u32)) | ||
917 | |||
914 | struct is_debug_frame_descriptor { | 918 | struct is_debug_frame_descriptor { |
915 | u32 sensor_frame_time; | 919 | u32 sensor_frame_time; |
916 | u32 sensor_exposure_time; | 920 | u32 sensor_exposure_time; |
@@ -988,6 +992,7 @@ struct sensor_open_extended { | |||
988 | struct fimc_is; | 992 | struct fimc_is; |
989 | 993 | ||
990 | int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is); | 994 | int fimc_is_hw_get_sensor_max_framerate(struct fimc_is *is); |
995 | int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset); | ||
991 | void fimc_is_set_initial_params(struct fimc_is *is); | 996 | void fimc_is_set_initial_params(struct fimc_is *is); |
992 | unsigned int __get_pending_param_count(struct fimc_is *is); | 997 | unsigned int __get_pending_param_count(struct fimc_is *is); |
993 | 998 | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index 2628733c4e10..cfe4406a83ff 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c | |||
@@ -105,6 +105,20 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num_args) | |||
105 | return 0; | 105 | return 0; |
106 | } | 106 | } |
107 | 107 | ||
108 | void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask) | ||
109 | { | ||
110 | if (hweight32(mask) == 1) { | ||
111 | dev_err(&is->pdev->dev, "%s(): not enough buffers (mask %#x)\n", | ||
112 | __func__, mask); | ||
113 | return; | ||
114 | } | ||
115 | |||
116 | if (mcuctl_read(is, MCUCTL_REG_ISSR(23)) != 0) | ||
117 | dev_dbg(&is->pdev->dev, "non-zero DMA buffer mask\n"); | ||
118 | |||
119 | mcuctl_write(mask, is, MCUCTL_REG_ISSR(23)); | ||
120 | } | ||
121 | |||
108 | void fimc_is_hw_set_sensor_num(struct fimc_is *is) | 122 | void fimc_is_hw_set_sensor_num(struct fimc_is *is) |
109 | { | 123 | { |
110 | pr_debug("setting sensor index to: %d\n", is->sensor_index); | 124 | pr_debug("setting sensor index to: %d\n", is->sensor_index); |
@@ -112,7 +126,7 @@ void fimc_is_hw_set_sensor_num(struct fimc_is *is) | |||
112 | mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0)); | 126 | mcuctl_write(IH_REPLY_DONE, is, MCUCTL_REG_ISSR(0)); |
113 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); | 127 | mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); |
114 | mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2)); | 128 | mcuctl_write(IHC_GET_SENSOR_NUM, is, MCUCTL_REG_ISSR(2)); |
115 | mcuctl_write(FIMC_IS_SENSOR_NUM, is, MCUCTL_REG_ISSR(3)); | 129 | mcuctl_write(FIMC_IS_SENSORS_NUM, is, MCUCTL_REG_ISSR(3)); |
116 | } | 130 | } |
117 | 131 | ||
118 | void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index) | 132 | void fimc_is_hw_close_sensor(struct fimc_is *is, unsigned int index) |
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/exynos4-is/fimc-is-regs.h index 1d9d4ffc6ad5..141e5ddadbeb 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.h +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.h | |||
@@ -147,6 +147,7 @@ int fimc_is_hw_get_params(struct fimc_is *is, unsigned int num); | |||
147 | void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is); | 147 | void fimc_is_hw_set_intgr0_gd0(struct fimc_is *is); |
148 | int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is); | 148 | int fimc_is_hw_wait_intmsr0_intmsd0(struct fimc_is *is); |
149 | void fimc_is_hw_set_sensor_num(struct fimc_is *is); | 149 | void fimc_is_hw_set_sensor_num(struct fimc_is *is); |
150 | void fimc_is_hw_set_isp_buf_mask(struct fimc_is *is, unsigned int mask); | ||
150 | void fimc_is_hw_stream_on(struct fimc_is *is); | 151 | void fimc_is_hw_stream_on(struct fimc_is *is); |
151 | void fimc_is_hw_stream_off(struct fimc_is *is); | 152 | void fimc_is_hw_stream_off(struct fimc_is *is); |
152 | int fimc_is_hw_set_param(struct fimc_is *is); | 153 | int fimc_is_hw_set_param(struct fimc_is *is); |
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/exynos4-is/fimc-is-sensor.c index 6647421e5d3a..10e82e21b5d1 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-sensor.c +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.c | |||
@@ -2,276 +2,21 @@ | |||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | 2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver |
3 | * | 3 | * |
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | 4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. |
5 | * | ||
6 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | 5 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> |
7 | * | 6 | * |
8 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
10 | * published by the Free Software Foundation. | 9 | * published by the Free Software Foundation. |
11 | */ | 10 | */ |
12 | #include <linux/delay.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/gpio.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of_gpio.h> | ||
20 | #include <linux/pm_runtime.h> | ||
21 | #include <linux/regulator/consumer.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <media/v4l2-subdev.h> | ||
24 | 11 | ||
25 | #include "fimc-is.h" | ||
26 | #include "fimc-is-sensor.h" | 12 | #include "fimc-is-sensor.h" |
27 | 13 | ||
28 | #define DRIVER_NAME "FIMC-IS-SENSOR" | ||
29 | |||
30 | static const char * const sensor_supply_names[] = { | ||
31 | "svdda", | ||
32 | "svddio", | ||
33 | }; | ||
34 | |||
35 | static const struct v4l2_mbus_framefmt fimc_is_sensor_formats[] = { | ||
36 | { | ||
37 | .code = V4L2_MBUS_FMT_SGRBG10_1X10, | ||
38 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
39 | .field = V4L2_FIELD_NONE, | ||
40 | } | ||
41 | }; | ||
42 | |||
43 | static const struct v4l2_mbus_framefmt *find_sensor_format( | ||
44 | struct v4l2_mbus_framefmt *mf) | ||
45 | { | ||
46 | int i; | ||
47 | |||
48 | for (i = 0; i < ARRAY_SIZE(fimc_is_sensor_formats); i++) | ||
49 | if (mf->code == fimc_is_sensor_formats[i].code) | ||
50 | return &fimc_is_sensor_formats[i]; | ||
51 | |||
52 | return &fimc_is_sensor_formats[0]; | ||
53 | } | ||
54 | |||
55 | static int fimc_is_sensor_enum_mbus_code(struct v4l2_subdev *sd, | ||
56 | struct v4l2_subdev_fh *fh, | ||
57 | struct v4l2_subdev_mbus_code_enum *code) | ||
58 | { | ||
59 | if (code->index >= ARRAY_SIZE(fimc_is_sensor_formats)) | ||
60 | return -EINVAL; | ||
61 | |||
62 | code->code = fimc_is_sensor_formats[code->index].code; | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static void fimc_is_sensor_try_format(struct fimc_is_sensor *sensor, | ||
67 | struct v4l2_mbus_framefmt *mf) | ||
68 | { | ||
69 | const struct sensor_drv_data *dd = sensor->drvdata; | ||
70 | const struct v4l2_mbus_framefmt *fmt; | ||
71 | |||
72 | fmt = find_sensor_format(mf); | ||
73 | mf->code = fmt->code; | ||
74 | v4l_bound_align_image(&mf->width, 16 + 8, dd->width, 0, | ||
75 | &mf->height, 12 + 8, dd->height, 0, 0); | ||
76 | } | ||
77 | |||
78 | static struct v4l2_mbus_framefmt *__fimc_is_sensor_get_format( | ||
79 | struct fimc_is_sensor *sensor, struct v4l2_subdev_fh *fh, | ||
80 | u32 pad, enum v4l2_subdev_format_whence which) | ||
81 | { | ||
82 | if (which == V4L2_SUBDEV_FORMAT_TRY) | ||
83 | return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL; | ||
84 | |||
85 | return &sensor->format; | ||
86 | } | ||
87 | |||
88 | static int fimc_is_sensor_set_fmt(struct v4l2_subdev *sd, | ||
89 | struct v4l2_subdev_fh *fh, | ||
90 | struct v4l2_subdev_format *fmt) | ||
91 | { | ||
92 | struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); | ||
93 | struct v4l2_mbus_framefmt *mf; | ||
94 | |||
95 | fimc_is_sensor_try_format(sensor, &fmt->format); | ||
96 | |||
97 | mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); | ||
98 | if (mf) { | ||
99 | mutex_lock(&sensor->lock); | ||
100 | if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) | ||
101 | *mf = fmt->format; | ||
102 | mutex_unlock(&sensor->lock); | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int fimc_is_sensor_get_fmt(struct v4l2_subdev *sd, | ||
108 | struct v4l2_subdev_fh *fh, | ||
109 | struct v4l2_subdev_format *fmt) | ||
110 | { | ||
111 | struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); | ||
112 | struct v4l2_mbus_framefmt *mf; | ||
113 | |||
114 | mf = __fimc_is_sensor_get_format(sensor, fh, fmt->pad, fmt->which); | ||
115 | |||
116 | mutex_lock(&sensor->lock); | ||
117 | fmt->format = *mf; | ||
118 | mutex_unlock(&sensor->lock); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = { | ||
123 | .enum_mbus_code = fimc_is_sensor_enum_mbus_code, | ||
124 | .get_fmt = fimc_is_sensor_get_fmt, | ||
125 | .set_fmt = fimc_is_sensor_set_fmt, | ||
126 | }; | ||
127 | |||
128 | static int fimc_is_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) | ||
129 | { | ||
130 | struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); | ||
131 | |||
132 | *format = fimc_is_sensor_formats[0]; | ||
133 | format->width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; | ||
134 | format->height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static const struct v4l2_subdev_internal_ops fimc_is_sensor_sd_internal_ops = { | ||
140 | .open = fimc_is_sensor_open, | ||
141 | }; | ||
142 | |||
143 | static int fimc_is_sensor_s_power(struct v4l2_subdev *sd, int on) | ||
144 | { | ||
145 | struct fimc_is_sensor *sensor = sd_to_fimc_is_sensor(sd); | ||
146 | int gpio = sensor->gpio_reset; | ||
147 | int ret; | ||
148 | |||
149 | if (on) { | ||
150 | ret = pm_runtime_get(sensor->dev); | ||
151 | if (ret < 0) | ||
152 | return ret; | ||
153 | |||
154 | ret = regulator_bulk_enable(SENSOR_NUM_SUPPLIES, | ||
155 | sensor->supplies); | ||
156 | if (ret < 0) { | ||
157 | pm_runtime_put(sensor->dev); | ||
158 | return ret; | ||
159 | } | ||
160 | if (gpio_is_valid(gpio)) { | ||
161 | gpio_set_value(gpio, 1); | ||
162 | usleep_range(600, 800); | ||
163 | gpio_set_value(gpio, 0); | ||
164 | usleep_range(10000, 11000); | ||
165 | gpio_set_value(gpio, 1); | ||
166 | } | ||
167 | |||
168 | /* A delay needed for the sensor initialization. */ | ||
169 | msleep(20); | ||
170 | } else { | ||
171 | if (gpio_is_valid(gpio)) | ||
172 | gpio_set_value(gpio, 0); | ||
173 | |||
174 | ret = regulator_bulk_disable(SENSOR_NUM_SUPPLIES, | ||
175 | sensor->supplies); | ||
176 | if (!ret) | ||
177 | pm_runtime_put(sensor->dev); | ||
178 | } | ||
179 | |||
180 | pr_info("%s:%d: on: %d, ret: %d\n", __func__, __LINE__, on, ret); | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = { | ||
186 | .s_power = fimc_is_sensor_s_power, | ||
187 | }; | ||
188 | |||
189 | static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = { | ||
190 | .core = &fimc_is_sensor_core_ops, | ||
191 | .pad = &fimc_is_sensor_pad_ops, | ||
192 | }; | ||
193 | |||
194 | static const struct of_device_id fimc_is_sensor_of_match[]; | ||
195 | |||
196 | static int fimc_is_sensor_probe(struct i2c_client *client, | ||
197 | const struct i2c_device_id *id) | ||
198 | { | ||
199 | struct device *dev = &client->dev; | ||
200 | struct fimc_is_sensor *sensor; | ||
201 | const struct of_device_id *of_id; | ||
202 | struct v4l2_subdev *sd; | ||
203 | int gpio, i, ret; | ||
204 | |||
205 | sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); | ||
206 | if (!sensor) | ||
207 | return -ENOMEM; | ||
208 | |||
209 | mutex_init(&sensor->lock); | ||
210 | sensor->gpio_reset = -EINVAL; | ||
211 | |||
212 | gpio = of_get_gpio_flags(dev->of_node, 0, NULL); | ||
213 | if (gpio_is_valid(gpio)) { | ||
214 | ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_LOW, | ||
215 | DRIVER_NAME); | ||
216 | if (ret < 0) | ||
217 | return ret; | ||
218 | } | ||
219 | sensor->gpio_reset = gpio; | ||
220 | |||
221 | for (i = 0; i < SENSOR_NUM_SUPPLIES; i++) | ||
222 | sensor->supplies[i].supply = sensor_supply_names[i]; | ||
223 | |||
224 | ret = devm_regulator_bulk_get(&client->dev, SENSOR_NUM_SUPPLIES, | ||
225 | sensor->supplies); | ||
226 | if (ret < 0) | ||
227 | return ret; | ||
228 | |||
229 | of_id = of_match_node(fimc_is_sensor_of_match, dev->of_node); | ||
230 | if (!of_id) | ||
231 | return -ENODEV; | ||
232 | |||
233 | sensor->drvdata = of_id->data; | ||
234 | sensor->dev = dev; | ||
235 | |||
236 | sd = &sensor->subdev; | ||
237 | v4l2_i2c_subdev_init(sd, client, &fimc_is_sensor_subdev_ops); | ||
238 | snprintf(sd->name, sizeof(sd->name), sensor->drvdata->subdev_name); | ||
239 | sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; | ||
240 | |||
241 | sensor->format.code = fimc_is_sensor_formats[0].code; | ||
242 | sensor->format.width = FIMC_IS_SENSOR_DEF_PIX_WIDTH; | ||
243 | sensor->format.height = FIMC_IS_SENSOR_DEF_PIX_HEIGHT; | ||
244 | |||
245 | sensor->pad.flags = MEDIA_PAD_FL_SOURCE; | ||
246 | ret = media_entity_init(&sd->entity, 1, &sensor->pad, 0); | ||
247 | if (ret < 0) | ||
248 | return ret; | ||
249 | |||
250 | pm_runtime_no_callbacks(dev); | ||
251 | pm_runtime_enable(dev); | ||
252 | |||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | static int fimc_is_sensor_remove(struct i2c_client *client) | ||
257 | { | ||
258 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
259 | media_entity_cleanup(&sd->entity); | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static const struct i2c_device_id fimc_is_sensor_ids[] = { | ||
264 | { } | ||
265 | }; | ||
266 | |||
267 | static const struct sensor_drv_data s5k6a3_drvdata = { | 14 | static const struct sensor_drv_data s5k6a3_drvdata = { |
268 | .id = FIMC_IS_SENSOR_ID_S5K6A3, | 15 | .id = FIMC_IS_SENSOR_ID_S5K6A3, |
269 | .subdev_name = "S5K6A3", | 16 | .open_timeout = S5K6A3_OPEN_TIMEOUT, |
270 | .width = S5K6A3_SENSOR_WIDTH, | ||
271 | .height = S5K6A3_SENSOR_HEIGHT, | ||
272 | }; | 17 | }; |
273 | 18 | ||
274 | static const struct of_device_id fimc_is_sensor_of_match[] = { | 19 | static const struct of_device_id fimc_is_sensor_of_ids[] = { |
275 | { | 20 | { |
276 | .compatible = "samsung,s5k6a3", | 21 | .compatible = "samsung,s5k6a3", |
277 | .data = &s5k6a3_drvdata, | 22 | .data = &s5k6a3_drvdata, |
@@ -279,27 +24,11 @@ static const struct of_device_id fimc_is_sensor_of_match[] = { | |||
279 | { } | 24 | { } |
280 | }; | 25 | }; |
281 | 26 | ||
282 | static struct i2c_driver fimc_is_sensor_driver = { | 27 | const struct sensor_drv_data *fimc_is_sensor_get_drvdata( |
283 | .driver = { | 28 | struct device_node *node) |
284 | .of_match_table = fimc_is_sensor_of_match, | ||
285 | .name = DRIVER_NAME, | ||
286 | .owner = THIS_MODULE, | ||
287 | }, | ||
288 | .probe = fimc_is_sensor_probe, | ||
289 | .remove = fimc_is_sensor_remove, | ||
290 | .id_table = fimc_is_sensor_ids, | ||
291 | }; | ||
292 | |||
293 | int fimc_is_register_sensor_driver(void) | ||
294 | { | 29 | { |
295 | return i2c_add_driver(&fimc_is_sensor_driver); | 30 | const struct of_device_id *of_id; |
296 | } | ||
297 | 31 | ||
298 | void fimc_is_unregister_sensor_driver(void) | 32 | of_id = of_match_node(fimc_is_sensor_of_ids, node); |
299 | { | 33 | return of_id ? of_id->data : NULL; |
300 | i2c_del_driver(&fimc_is_sensor_driver); | ||
301 | } | 34 | } |
302 | |||
303 | MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); | ||
304 | MODULE_DESCRIPTION("Exynos4x12 FIMC-IS image sensor subdev driver"); | ||
305 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/exynos4-is/fimc-is-sensor.h index 6036d49a6c68..173ccffa4bcd 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-sensor.h +++ b/drivers/media/platform/exynos4-is/fimc-is-sensor.h | |||
@@ -13,24 +13,13 @@ | |||
13 | #ifndef FIMC_IS_SENSOR_H_ | 13 | #ifndef FIMC_IS_SENSOR_H_ |
14 | #define FIMC_IS_SENSOR_H_ | 14 | #define FIMC_IS_SENSOR_H_ |
15 | 15 | ||
16 | #include <linux/clk.h> | 16 | #include <linux/of.h> |
17 | #include <linux/device.h> | 17 | #include <linux/types.h> |
18 | #include <linux/kernel.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/regulator/consumer.h> | ||
21 | #include <linux/videodev2.h> | ||
22 | #include <media/v4l2-subdev.h> | ||
23 | |||
24 | #define FIMC_IS_SENSOR_OPEN_TIMEOUT 2000 /* ms */ | ||
25 | |||
26 | #define FIMC_IS_SENSOR_DEF_PIX_WIDTH 1296 | ||
27 | #define FIMC_IS_SENSOR_DEF_PIX_HEIGHT 732 | ||
28 | 18 | ||
19 | #define S5K6A3_OPEN_TIMEOUT 2000 /* ms */ | ||
29 | #define S5K6A3_SENSOR_WIDTH 1392 | 20 | #define S5K6A3_SENSOR_WIDTH 1392 |
30 | #define S5K6A3_SENSOR_HEIGHT 1392 | 21 | #define S5K6A3_SENSOR_HEIGHT 1392 |
31 | 22 | ||
32 | #define SENSOR_NUM_SUPPLIES 2 | ||
33 | |||
34 | enum fimc_is_sensor_id { | 23 | enum fimc_is_sensor_id { |
35 | FIMC_IS_SENSOR_ID_S5K3H2 = 1, | 24 | FIMC_IS_SENSOR_ID_S5K3H2 = 1, |
36 | FIMC_IS_SENSOR_ID_S5K6A3, | 25 | FIMC_IS_SENSOR_ID_S5K6A3, |
@@ -45,45 +34,23 @@ enum fimc_is_sensor_id { | |||
45 | 34 | ||
46 | struct sensor_drv_data { | 35 | struct sensor_drv_data { |
47 | enum fimc_is_sensor_id id; | 36 | enum fimc_is_sensor_id id; |
48 | const char * const subdev_name; | 37 | /* sensor open timeout in ms */ |
49 | unsigned int width; | 38 | unsigned short open_timeout; |
50 | unsigned int height; | ||
51 | }; | 39 | }; |
52 | 40 | ||
53 | /** | 41 | /** |
54 | * struct fimc_is_sensor - fimc-is sensor data structure | 42 | * struct fimc_is_sensor - fimc-is sensor data structure |
55 | * @dev: pointer to this I2C client device structure | ||
56 | * @subdev: the image sensor's v4l2 subdev | ||
57 | * @pad: subdev media source pad | ||
58 | * @supplies: image sensor's voltage regulator supplies | ||
59 | * @gpio_reset: GPIO connected to the sensor's reset pin | ||
60 | * @drvdata: a pointer to the sensor's parameters data structure | 43 | * @drvdata: a pointer to the sensor's parameters data structure |
61 | * @i2c_bus: ISP I2C bus index (0...1) | 44 | * @i2c_bus: ISP I2C bus index (0...1) |
62 | * @test_pattern: true to enable video test pattern | 45 | * @test_pattern: true to enable video test pattern |
63 | * @lock: mutex protecting the structure's members below | ||
64 | * @format: media bus format at the sensor's source pad | ||
65 | */ | 46 | */ |
66 | struct fimc_is_sensor { | 47 | struct fimc_is_sensor { |
67 | struct device *dev; | ||
68 | struct v4l2_subdev subdev; | ||
69 | struct media_pad pad; | ||
70 | struct regulator_bulk_data supplies[SENSOR_NUM_SUPPLIES]; | ||
71 | int gpio_reset; | ||
72 | const struct sensor_drv_data *drvdata; | 48 | const struct sensor_drv_data *drvdata; |
73 | unsigned int i2c_bus; | 49 | unsigned int i2c_bus; |
74 | bool test_pattern; | 50 | u8 test_pattern; |
75 | |||
76 | struct mutex lock; | ||
77 | struct v4l2_mbus_framefmt format; | ||
78 | }; | 51 | }; |
79 | 52 | ||
80 | static inline | 53 | const struct sensor_drv_data *fimc_is_sensor_get_drvdata( |
81 | struct fimc_is_sensor *sd_to_fimc_is_sensor(struct v4l2_subdev *sd) | 54 | struct device_node *node); |
82 | { | ||
83 | return container_of(sd, struct fimc_is_sensor, subdev); | ||
84 | } | ||
85 | |||
86 | int fimc_is_register_sensor_driver(void); | ||
87 | void fimc_is_unregister_sensor_driver(void); | ||
88 | 55 | ||
89 | #endif /* FIMC_IS_SENSOR_H_ */ | 56 | #endif /* FIMC_IS_SENSOR_H_ */ |
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 9bdfa4599bc3..128b73b6cce2 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c | |||
@@ -161,78 +161,69 @@ static void fimc_is_disable_clocks(struct fimc_is *is) | |||
161 | } | 161 | } |
162 | } | 162 | } |
163 | 163 | ||
164 | static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor, | 164 | static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, |
165 | struct device_node *np) | 165 | struct device_node *node) |
166 | { | 166 | { |
167 | struct fimc_is_sensor *sensor = &is->sensor[index]; | ||
167 | u32 tmp = 0; | 168 | u32 tmp = 0; |
168 | int ret; | 169 | int ret; |
169 | 170 | ||
170 | np = of_graph_get_next_endpoint(np, NULL); | 171 | sensor->drvdata = fimc_is_sensor_get_drvdata(node); |
171 | if (!np) | 172 | if (!sensor->drvdata) { |
173 | dev_err(&is->pdev->dev, "no driver data found for: %s\n", | ||
174 | node->full_name); | ||
175 | return -EINVAL; | ||
176 | } | ||
177 | |||
178 | node = of_graph_get_next_endpoint(node, NULL); | ||
179 | if (!node) | ||
172 | return -ENXIO; | 180 | return -ENXIO; |
173 | np = of_graph_get_remote_port(np); | 181 | |
174 | if (!np) | 182 | node = of_graph_get_remote_port(node); |
183 | if (!node) | ||
175 | return -ENXIO; | 184 | return -ENXIO; |
176 | 185 | ||
177 | /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ | 186 | /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ |
178 | ret = of_property_read_u32(np, "reg", &tmp); | 187 | ret = of_property_read_u32(node, "reg", &tmp); |
179 | sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; | 188 | if (ret < 0) { |
189 | dev_err(&is->pdev->dev, "reg property not found at: %s\n", | ||
190 | node->full_name); | ||
191 | return ret; | ||
192 | } | ||
180 | 193 | ||
181 | return ret; | 194 | sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; |
195 | return 0; | ||
182 | } | 196 | } |
183 | 197 | ||
184 | static int fimc_is_register_subdevs(struct fimc_is *is) | 198 | static int fimc_is_register_subdevs(struct fimc_is *is) |
185 | { | 199 | { |
186 | struct device_node *adapter, *child; | 200 | struct device_node *i2c_bus, *child; |
187 | int ret; | 201 | int ret, index = 0; |
188 | 202 | ||
189 | ret = fimc_isp_subdev_create(&is->isp); | 203 | ret = fimc_isp_subdev_create(&is->isp); |
190 | if (ret < 0) | 204 | if (ret < 0) |
191 | return ret; | 205 | return ret; |
192 | 206 | ||
193 | for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) { | 207 | /* Initialize memory allocator context for the ISP DMA. */ |
194 | if (!of_find_device_by_node(adapter)) { | 208 | is->isp.alloc_ctx = is->alloc_ctx; |
195 | of_node_put(adapter); | ||
196 | return -EPROBE_DEFER; | ||
197 | } | ||
198 | |||
199 | for_each_available_child_of_node(adapter, child) { | ||
200 | struct i2c_client *client; | ||
201 | struct v4l2_subdev *sd; | ||
202 | |||
203 | client = of_find_i2c_device_by_node(child); | ||
204 | if (!client) | ||
205 | goto e_retry; | ||
206 | |||
207 | sd = i2c_get_clientdata(client); | ||
208 | if (!sd) | ||
209 | goto e_retry; | ||
210 | 209 | ||
211 | /* FIXME: Add support for multiple sensors. */ | 210 | for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) { |
212 | if (WARN_ON(is->sensor)) | 211 | for_each_available_child_of_node(i2c_bus, child) { |
213 | continue; | 212 | ret = fimc_is_parse_sensor_config(is, index, child); |
214 | 213 | ||
215 | is->sensor = sd_to_fimc_is_sensor(sd); | 214 | if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) { |
216 | 215 | of_node_put(child); | |
217 | if (fimc_is_parse_sensor_config(is->sensor, child)) { | 216 | return ret; |
218 | dev_warn(&is->pdev->dev, "DT parse error: %s\n", | ||
219 | child->full_name); | ||
220 | } | 217 | } |
221 | pr_debug("%s(): registered subdev: %p\n", | 218 | index++; |
222 | __func__, sd->name); | ||
223 | } | 219 | } |
224 | } | 220 | } |
225 | return 0; | 221 | return 0; |
226 | |||
227 | e_retry: | ||
228 | of_node_put(child); | ||
229 | return -EPROBE_DEFER; | ||
230 | } | 222 | } |
231 | 223 | ||
232 | static int fimc_is_unregister_subdevs(struct fimc_is *is) | 224 | static int fimc_is_unregister_subdevs(struct fimc_is *is) |
233 | { | 225 | { |
234 | fimc_isp_subdev_destroy(&is->isp); | 226 | fimc_isp_subdev_destroy(&is->isp); |
235 | is->sensor = NULL; | ||
236 | return 0; | 227 | return 0; |
237 | } | 228 | } |
238 | 229 | ||
@@ -647,7 +638,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is, | |||
647 | fimc_is_hw_set_intgr0_gd0(is); | 638 | fimc_is_hw_set_intgr0_gd0(is); |
648 | 639 | ||
649 | return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, | 640 | return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, |
650 | FIMC_IS_SENSOR_OPEN_TIMEOUT); | 641 | sensor->drvdata->open_timeout); |
651 | } | 642 | } |
652 | 643 | ||
653 | 644 | ||
@@ -661,8 +652,8 @@ int fimc_is_hw_initialize(struct fimc_is *is) | |||
661 | u32 prev_id; | 652 | u32 prev_id; |
662 | int i, ret; | 653 | int i, ret; |
663 | 654 | ||
664 | /* Sensor initialization. */ | 655 | /* Sensor initialization. Only one sensor is currently supported. */ |
665 | ret = fimc_is_hw_open_sensor(is, is->sensor); | 656 | ret = fimc_is_hw_open_sensor(is, &is->sensor[0]); |
666 | if (ret < 0) | 657 | if (ret < 0) |
667 | return ret; | 658 | return ret; |
668 | 659 | ||
@@ -977,27 +968,20 @@ static int fimc_is_module_init(void) | |||
977 | { | 968 | { |
978 | int ret; | 969 | int ret; |
979 | 970 | ||
980 | ret = fimc_is_register_sensor_driver(); | ||
981 | if (ret < 0) | ||
982 | return ret; | ||
983 | |||
984 | ret = fimc_is_register_i2c_driver(); | 971 | ret = fimc_is_register_i2c_driver(); |
985 | if (ret < 0) | 972 | if (ret < 0) |
986 | goto err_sens; | 973 | return ret; |
987 | 974 | ||
988 | ret = platform_driver_register(&fimc_is_driver); | 975 | ret = platform_driver_register(&fimc_is_driver); |
989 | if (!ret) | ||
990 | return ret; | ||
991 | 976 | ||
992 | fimc_is_unregister_i2c_driver(); | 977 | if (ret < 0) |
993 | err_sens: | 978 | fimc_is_unregister_i2c_driver(); |
994 | fimc_is_unregister_sensor_driver(); | 979 | |
995 | return ret; | 980 | return ret; |
996 | } | 981 | } |
997 | 982 | ||
998 | static void fimc_is_module_exit(void) | 983 | static void fimc_is_module_exit(void) |
999 | { | 984 | { |
1000 | fimc_is_unregister_sensor_driver(); | ||
1001 | fimc_is_unregister_i2c_driver(); | 985 | fimc_is_unregister_i2c_driver(); |
1002 | platform_driver_unregister(&fimc_is_driver); | 986 | platform_driver_unregister(&fimc_is_driver); |
1003 | } | 987 | } |
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index 61bb0127e19d..e0be691af2d3 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h | |||
@@ -39,7 +39,7 @@ | |||
39 | #define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */ | 39 | #define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */ |
40 | #define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */ | 40 | #define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */ |
41 | 41 | ||
42 | #define FIMC_IS_SENSOR_NUM 2 | 42 | #define FIMC_IS_SENSORS_NUM 2 |
43 | 43 | ||
44 | /* Memory definitions */ | 44 | /* Memory definitions */ |
45 | #define FIMC_IS_CPU_MEM_SIZE (0xa00000) | 45 | #define FIMC_IS_CPU_MEM_SIZE (0xa00000) |
@@ -253,7 +253,7 @@ struct fimc_is { | |||
253 | struct firmware *f_w; | 253 | struct firmware *f_w; |
254 | 254 | ||
255 | struct fimc_isp isp; | 255 | struct fimc_isp isp; |
256 | struct fimc_is_sensor *sensor; | 256 | struct fimc_is_sensor sensor[FIMC_IS_SENSORS_NUM]; |
257 | struct fimc_is_setfile setfile; | 257 | struct fimc_is_setfile setfile; |
258 | 258 | ||
259 | struct vb2_alloc_ctx *alloc_ctx; | 259 | struct vb2_alloc_ctx *alloc_ctx; |
@@ -292,6 +292,11 @@ static inline struct fimc_is *fimc_isp_to_is(struct fimc_isp *isp) | |||
292 | return container_of(isp, struct fimc_is, isp); | 292 | return container_of(isp, struct fimc_is, isp); |
293 | } | 293 | } |
294 | 294 | ||
295 | static inline struct chain_config *__get_curr_is_config(struct fimc_is *is) | ||
296 | { | ||
297 | return &is->config[is->config_index]; | ||
298 | } | ||
299 | |||
295 | static inline void fimc_is_mem_barrier(void) | 300 | static inline void fimc_is_mem_barrier(void) |
296 | { | 301 | { |
297 | mb(); | 302 | mb(); |
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/exynos4-is/fimc-isp-video.c new file mode 100644 index 000000000000..e92b4e115adb --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.c | |||
@@ -0,0 +1,660 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * FIMC-IS ISP video input and video output DMA interface driver | ||
5 | * | ||
6 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
7 | * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
8 | * | ||
9 | * The hardware handling code derived from a driver written by | ||
10 | * Younghwan Joo <yhwan.joo@samsung.com>. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/bitops.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/delay.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/printk.h> | ||
25 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/videodev2.h> | ||
28 | |||
29 | #include <media/v4l2-device.h> | ||
30 | #include <media/v4l2-ioctl.h> | ||
31 | #include <media/videobuf2-core.h> | ||
32 | #include <media/videobuf2-dma-contig.h> | ||
33 | #include <media/s5p_fimc.h> | ||
34 | |||
35 | #include "common.h" | ||
36 | #include "media-dev.h" | ||
37 | #include "fimc-is.h" | ||
38 | #include "fimc-isp-video.h" | ||
39 | #include "fimc-is-param.h" | ||
40 | |||
41 | static int isp_video_capture_queue_setup(struct vb2_queue *vq, | ||
42 | const struct v4l2_format *pfmt, | ||
43 | unsigned int *num_buffers, unsigned int *num_planes, | ||
44 | unsigned int sizes[], void *allocators[]) | ||
45 | { | ||
46 | struct fimc_isp *isp = vb2_get_drv_priv(vq); | ||
47 | struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt; | ||
48 | const struct v4l2_pix_format_mplane *pixm = NULL; | ||
49 | const struct fimc_fmt *fmt; | ||
50 | unsigned int wh, i; | ||
51 | |||
52 | if (pfmt) { | ||
53 | pixm = &pfmt->fmt.pix_mp; | ||
54 | fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, -1); | ||
55 | wh = pixm->width * pixm->height; | ||
56 | } else { | ||
57 | fmt = isp->video_capture.format; | ||
58 | wh = vid_fmt->width * vid_fmt->height; | ||
59 | } | ||
60 | |||
61 | if (fmt == NULL) | ||
62 | return -EINVAL; | ||
63 | |||
64 | *num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN, | ||
65 | FIMC_ISP_REQ_BUFS_MAX); | ||
66 | *num_planes = fmt->memplanes; | ||
67 | |||
68 | for (i = 0; i < fmt->memplanes; i++) { | ||
69 | unsigned int size = (wh * fmt->depth[i]) / 8; | ||
70 | if (pixm) | ||
71 | sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); | ||
72 | else | ||
73 | sizes[i] = size; | ||
74 | allocators[i] = isp->alloc_ctx; | ||
75 | } | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is) | ||
81 | { | ||
82 | return &__get_curr_is_config(is)->isp.dma2_output; | ||
83 | } | ||
84 | |||
85 | static int isp_video_capture_start_streaming(struct vb2_queue *q, | ||
86 | unsigned int count) | ||
87 | { | ||
88 | struct fimc_isp *isp = vb2_get_drv_priv(q); | ||
89 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
90 | struct param_dma_output *dma = __get_isp_dma2(is); | ||
91 | struct fimc_is_video *video = &isp->video_capture; | ||
92 | int ret; | ||
93 | |||
94 | if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) || | ||
95 | test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) | ||
96 | return 0; | ||
97 | |||
98 | |||
99 | dma->cmd = DMA_OUTPUT_COMMAND_ENABLE; | ||
100 | dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE; | ||
101 | dma->buffer_address = is->is_dma_p_region + | ||
102 | DMA2_OUTPUT_ADDR_ARRAY_OFFS; | ||
103 | dma->buffer_number = video->reqbufs_count; | ||
104 | dma->dma_out_mask = video->buf_mask; | ||
105 | |||
106 | isp_dbg(2, &video->ve.vdev, | ||
107 | "buf_count: %d, planes: %d, dma addr table: %#x\n", | ||
108 | video->buf_count, video->format->memplanes, | ||
109 | dma->buffer_address); | ||
110 | |||
111 | fimc_is_mem_barrier(); | ||
112 | |||
113 | fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); | ||
114 | __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); | ||
115 | |||
116 | ret = fimc_is_itf_s_param(is, false); | ||
117 | if (ret < 0) | ||
118 | return ret; | ||
119 | |||
120 | ret = fimc_pipeline_call(&video->ve, set_stream, 1); | ||
121 | if (ret < 0) | ||
122 | return ret; | ||
123 | |||
124 | set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); | ||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | static int isp_video_capture_stop_streaming(struct vb2_queue *q) | ||
129 | { | ||
130 | struct fimc_isp *isp = vb2_get_drv_priv(q); | ||
131 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
132 | struct param_dma_output *dma = __get_isp_dma2(is); | ||
133 | int ret; | ||
134 | |||
135 | ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0); | ||
136 | if (ret < 0) | ||
137 | return ret; | ||
138 | |||
139 | dma->cmd = DMA_OUTPUT_COMMAND_DISABLE; | ||
140 | dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE; | ||
141 | dma->buffer_number = 0; | ||
142 | dma->buffer_address = 0; | ||
143 | dma->dma_out_mask = 0; | ||
144 | |||
145 | fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT); | ||
146 | __fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT); | ||
147 | |||
148 | ret = fimc_is_itf_s_param(is, false); | ||
149 | if (ret < 0) | ||
150 | dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__); | ||
151 | |||
152 | fimc_is_hw_set_isp_buf_mask(is, 0); | ||
153 | |||
154 | clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); | ||
155 | clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state); | ||
156 | |||
157 | isp->video_capture.buf_count = 0; | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb) | ||
162 | { | ||
163 | struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); | ||
164 | struct fimc_is_video *video = &isp->video_capture; | ||
165 | int i; | ||
166 | |||
167 | if (video->format == NULL) | ||
168 | return -EINVAL; | ||
169 | |||
170 | for (i = 0; i < video->format->memplanes; i++) { | ||
171 | unsigned long size = video->pixfmt.plane_fmt[i].sizeimage; | ||
172 | |||
173 | if (vb2_plane_size(vb, i) < size) { | ||
174 | v4l2_err(&video->ve.vdev, | ||
175 | "User buffer too small (%ld < %ld)\n", | ||
176 | vb2_plane_size(vb, i), size); | ||
177 | return -EINVAL; | ||
178 | } | ||
179 | vb2_set_plane_payload(vb, i, size); | ||
180 | } | ||
181 | |||
182 | /* Check if we get one of the already known buffers. */ | ||
183 | if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { | ||
184 | dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); | ||
185 | int i; | ||
186 | |||
187 | for (i = 0; i < video->buf_count; i++) | ||
188 | if (video->buffers[i]->dma_addr[0] == dma_addr) | ||
189 | return 0; | ||
190 | return -ENXIO; | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static void isp_video_capture_buffer_queue(struct vb2_buffer *vb) | ||
197 | { | ||
198 | struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue); | ||
199 | struct fimc_is_video *video = &isp->video_capture; | ||
200 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
201 | struct isp_video_buf *ivb = to_isp_video_buf(vb); | ||
202 | unsigned long flags; | ||
203 | unsigned int i; | ||
204 | |||
205 | if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) { | ||
206 | spin_lock_irqsave(&is->slock, flags); | ||
207 | video->buf_mask |= BIT(ivb->index); | ||
208 | spin_unlock_irqrestore(&is->slock, flags); | ||
209 | } else { | ||
210 | unsigned int num_planes = video->format->memplanes; | ||
211 | |||
212 | ivb->index = video->buf_count; | ||
213 | video->buffers[ivb->index] = ivb; | ||
214 | |||
215 | for (i = 0; i < num_planes; i++) { | ||
216 | int buf_index = ivb->index * num_planes + i; | ||
217 | |||
218 | ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i); | ||
219 | is->is_p_region->shared[32 + buf_index] = | ||
220 | ivb->dma_addr[i]; | ||
221 | |||
222 | isp_dbg(2, &video->ve.vdev, | ||
223 | "dma_buf %d (%d/%d/%d) addr: %#x\n", | ||
224 | buf_index, ivb->index, i, vb->v4l2_buf.index, | ||
225 | ivb->dma_addr[i]); | ||
226 | } | ||
227 | |||
228 | if (++video->buf_count < video->reqbufs_count) | ||
229 | return; | ||
230 | |||
231 | video->buf_mask = (1UL << video->buf_count) - 1; | ||
232 | set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state); | ||
233 | } | ||
234 | |||
235 | if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state)) | ||
236 | isp_video_capture_start_streaming(vb->vb2_queue, 0); | ||
237 | } | ||
238 | |||
239 | /* | ||
240 | * FIMC-IS ISP input and output DMA interface interrupt handler. | ||
241 | * Locking: called with is->slock spinlock held. | ||
242 | */ | ||
243 | void fimc_isp_video_irq_handler(struct fimc_is *is) | ||
244 | { | ||
245 | struct fimc_is_video *video = &is->isp.video_capture; | ||
246 | struct vb2_buffer *vb; | ||
247 | int buf_index; | ||
248 | |||
249 | /* TODO: Ensure the DMA is really stopped in stop_streaming callback */ | ||
250 | if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state)) | ||
251 | return; | ||
252 | |||
253 | buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count; | ||
254 | vb = &video->buffers[buf_index]->vb; | ||
255 | |||
256 | v4l2_get_timestamp(&vb->v4l2_buf.timestamp); | ||
257 | vb2_buffer_done(vb, VB2_BUF_STATE_DONE); | ||
258 | |||
259 | video->buf_mask &= ~BIT(buf_index); | ||
260 | fimc_is_hw_set_isp_buf_mask(is, video->buf_mask); | ||
261 | } | ||
262 | |||
263 | static const struct vb2_ops isp_video_capture_qops = { | ||
264 | .queue_setup = isp_video_capture_queue_setup, | ||
265 | .buf_prepare = isp_video_capture_buffer_prepare, | ||
266 | .buf_queue = isp_video_capture_buffer_queue, | ||
267 | .wait_prepare = vb2_ops_wait_prepare, | ||
268 | .wait_finish = vb2_ops_wait_finish, | ||
269 | .start_streaming = isp_video_capture_start_streaming, | ||
270 | .stop_streaming = isp_video_capture_stop_streaming, | ||
271 | }; | ||
272 | |||
273 | static int isp_video_open(struct file *file) | ||
274 | { | ||
275 | struct fimc_isp *isp = video_drvdata(file); | ||
276 | struct exynos_video_entity *ve = &isp->video_capture.ve; | ||
277 | struct media_entity *me = &ve->vdev.entity; | ||
278 | int ret; | ||
279 | |||
280 | if (mutex_lock_interruptible(&isp->video_lock)) | ||
281 | return -ERESTARTSYS; | ||
282 | |||
283 | ret = v4l2_fh_open(file); | ||
284 | if (ret < 0) | ||
285 | goto unlock; | ||
286 | |||
287 | ret = pm_runtime_get_sync(&isp->pdev->dev); | ||
288 | if (ret < 0) | ||
289 | goto rel_fh; | ||
290 | |||
291 | if (v4l2_fh_is_singular_file(file)) { | ||
292 | mutex_lock(&me->parent->graph_mutex); | ||
293 | |||
294 | ret = fimc_pipeline_call(ve, open, me, true); | ||
295 | |||
296 | /* Mark the video pipeline as in use. */ | ||
297 | if (ret == 0) | ||
298 | me->use_count++; | ||
299 | |||
300 | mutex_unlock(&me->parent->graph_mutex); | ||
301 | } | ||
302 | if (!ret) | ||
303 | goto unlock; | ||
304 | rel_fh: | ||
305 | v4l2_fh_release(file); | ||
306 | unlock: | ||
307 | mutex_unlock(&isp->video_lock); | ||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | static int isp_video_release(struct file *file) | ||
312 | { | ||
313 | struct fimc_isp *isp = video_drvdata(file); | ||
314 | struct fimc_is_video *ivc = &isp->video_capture; | ||
315 | struct media_entity *entity = &ivc->ve.vdev.entity; | ||
316 | struct media_device *mdev = entity->parent; | ||
317 | int ret = 0; | ||
318 | |||
319 | mutex_lock(&isp->video_lock); | ||
320 | |||
321 | if (v4l2_fh_is_singular_file(file) && ivc->streaming) { | ||
322 | media_entity_pipeline_stop(entity); | ||
323 | ivc->streaming = 0; | ||
324 | } | ||
325 | |||
326 | vb2_fop_release(file); | ||
327 | |||
328 | if (v4l2_fh_is_singular_file(file)) { | ||
329 | fimc_pipeline_call(&ivc->ve, close); | ||
330 | |||
331 | mutex_lock(&mdev->graph_mutex); | ||
332 | entity->use_count--; | ||
333 | mutex_unlock(&mdev->graph_mutex); | ||
334 | } | ||
335 | |||
336 | pm_runtime_put(&isp->pdev->dev); | ||
337 | mutex_unlock(&isp->video_lock); | ||
338 | |||
339 | return ret; | ||
340 | } | ||
341 | |||
342 | static const struct v4l2_file_operations isp_video_fops = { | ||
343 | .owner = THIS_MODULE, | ||
344 | .open = isp_video_open, | ||
345 | .release = isp_video_release, | ||
346 | .poll = vb2_fop_poll, | ||
347 | .unlocked_ioctl = video_ioctl2, | ||
348 | .mmap = vb2_fop_mmap, | ||
349 | }; | ||
350 | |||
351 | /* | ||
352 | * Video node ioctl operations | ||
353 | */ | ||
354 | static int isp_video_querycap(struct file *file, void *priv, | ||
355 | struct v4l2_capability *cap) | ||
356 | { | ||
357 | struct fimc_isp *isp = video_drvdata(file); | ||
358 | |||
359 | __fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int isp_video_enum_fmt_mplane(struct file *file, void *priv, | ||
364 | struct v4l2_fmtdesc *f) | ||
365 | { | ||
366 | const struct fimc_fmt *fmt; | ||
367 | |||
368 | if (f->index >= FIMC_ISP_NUM_FORMATS) | ||
369 | return -EINVAL; | ||
370 | |||
371 | fmt = fimc_isp_find_format(NULL, NULL, f->index); | ||
372 | if (WARN_ON(fmt == NULL)) | ||
373 | return -EINVAL; | ||
374 | |||
375 | strlcpy(f->description, fmt->name, sizeof(f->description)); | ||
376 | f->pixelformat = fmt->fourcc; | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int isp_video_g_fmt_mplane(struct file *file, void *fh, | ||
382 | struct v4l2_format *f) | ||
383 | { | ||
384 | struct fimc_isp *isp = video_drvdata(file); | ||
385 | |||
386 | f->fmt.pix_mp = isp->video_capture.pixfmt; | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static void __isp_video_try_fmt(struct fimc_isp *isp, | ||
391 | struct v4l2_pix_format_mplane *pixm, | ||
392 | const struct fimc_fmt **fmt) | ||
393 | { | ||
394 | *fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2); | ||
395 | |||
396 | pixm->colorspace = V4L2_COLORSPACE_SRGB; | ||
397 | pixm->field = V4L2_FIELD_NONE; | ||
398 | pixm->num_planes = (*fmt)->memplanes; | ||
399 | pixm->pixelformat = (*fmt)->fourcc; | ||
400 | /* | ||
401 | * TODO: double check with the docmentation these width/height | ||
402 | * constraints are correct. | ||
403 | */ | ||
404 | v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN, | ||
405 | FIMC_ISP_SOURCE_WIDTH_MAX, 3, | ||
406 | &pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN, | ||
407 | FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0); | ||
408 | } | ||
409 | |||
410 | static int isp_video_try_fmt_mplane(struct file *file, void *fh, | ||
411 | struct v4l2_format *f) | ||
412 | { | ||
413 | struct fimc_isp *isp = video_drvdata(file); | ||
414 | |||
415 | __isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL); | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int isp_video_s_fmt_mplane(struct file *file, void *priv, | ||
420 | struct v4l2_format *f) | ||
421 | { | ||
422 | struct fimc_isp *isp = video_drvdata(file); | ||
423 | struct fimc_is *is = fimc_isp_to_is(isp); | ||
424 | struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; | ||
425 | const struct fimc_fmt *ifmt = NULL; | ||
426 | struct param_dma_output *dma = __get_isp_dma2(is); | ||
427 | |||
428 | __isp_video_try_fmt(isp, pixm, &ifmt); | ||
429 | |||
430 | if (WARN_ON(ifmt == NULL)) | ||
431 | return -EINVAL; | ||
432 | |||
433 | dma->format = DMA_OUTPUT_FORMAT_BAYER; | ||
434 | dma->order = DMA_OUTPUT_ORDER_GB_BG; | ||
435 | dma->plane = ifmt->memplanes; | ||
436 | dma->bitwidth = ifmt->depth[0]; | ||
437 | dma->width = pixm->width; | ||
438 | dma->height = pixm->height; | ||
439 | |||
440 | fimc_is_mem_barrier(); | ||
441 | |||
442 | isp->video_capture.format = ifmt; | ||
443 | isp->video_capture.pixfmt = *pixm; | ||
444 | |||
445 | return 0; | ||
446 | } | ||
447 | |||
448 | /* | ||
449 | * Check for source/sink format differences at each link. | ||
450 | * Return 0 if the formats match or -EPIPE otherwise. | ||
451 | */ | ||
452 | static int isp_video_pipeline_validate(struct fimc_isp *isp) | ||
453 | { | ||
454 | struct v4l2_subdev *sd = &isp->subdev; | ||
455 | struct v4l2_subdev_format sink_fmt, src_fmt; | ||
456 | struct media_pad *pad; | ||
457 | int ret; | ||
458 | |||
459 | while (1) { | ||
460 | /* Retrieve format at the sink pad */ | ||
461 | pad = &sd->entity.pads[0]; | ||
462 | if (!(pad->flags & MEDIA_PAD_FL_SINK)) | ||
463 | break; | ||
464 | sink_fmt.pad = pad->index; | ||
465 | sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
466 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt); | ||
467 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
468 | return -EPIPE; | ||
469 | |||
470 | /* Retrieve format at the source pad */ | ||
471 | pad = media_entity_remote_pad(pad); | ||
472 | if (pad == NULL || | ||
473 | media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) | ||
474 | break; | ||
475 | |||
476 | sd = media_entity_to_v4l2_subdev(pad->entity); | ||
477 | src_fmt.pad = pad->index; | ||
478 | src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; | ||
479 | ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); | ||
480 | if (ret < 0 && ret != -ENOIOCTLCMD) | ||
481 | return -EPIPE; | ||
482 | |||
483 | if (src_fmt.format.width != sink_fmt.format.width || | ||
484 | src_fmt.format.height != sink_fmt.format.height || | ||
485 | src_fmt.format.code != sink_fmt.format.code) | ||
486 | return -EPIPE; | ||
487 | } | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int isp_video_streamon(struct file *file, void *priv, | ||
493 | enum v4l2_buf_type type) | ||
494 | { | ||
495 | struct fimc_isp *isp = video_drvdata(file); | ||
496 | struct exynos_video_entity *ve = &isp->video_capture.ve; | ||
497 | struct media_entity *me = &ve->vdev.entity; | ||
498 | int ret; | ||
499 | |||
500 | ret = media_entity_pipeline_start(me, &ve->pipe->mp); | ||
501 | if (ret < 0) | ||
502 | return ret; | ||
503 | |||
504 | ret = isp_video_pipeline_validate(isp); | ||
505 | if (ret < 0) | ||
506 | goto p_stop; | ||
507 | |||
508 | ret = vb2_ioctl_streamon(file, priv, type); | ||
509 | if (ret < 0) | ||
510 | goto p_stop; | ||
511 | |||
512 | isp->video_capture.streaming = 1; | ||
513 | return 0; | ||
514 | p_stop: | ||
515 | media_entity_pipeline_stop(me); | ||
516 | return ret; | ||
517 | } | ||
518 | |||
519 | static int isp_video_streamoff(struct file *file, void *priv, | ||
520 | enum v4l2_buf_type type) | ||
521 | { | ||
522 | struct fimc_isp *isp = video_drvdata(file); | ||
523 | struct fimc_is_video *video = &isp->video_capture; | ||
524 | int ret; | ||
525 | |||
526 | ret = vb2_ioctl_streamoff(file, priv, type); | ||
527 | if (ret < 0) | ||
528 | return ret; | ||
529 | |||
530 | media_entity_pipeline_stop(&video->ve.vdev.entity); | ||
531 | video->streaming = 0; | ||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | static int isp_video_reqbufs(struct file *file, void *priv, | ||
536 | struct v4l2_requestbuffers *rb) | ||
537 | { | ||
538 | struct fimc_isp *isp = video_drvdata(file); | ||
539 | int ret; | ||
540 | |||
541 | ret = vb2_ioctl_reqbufs(file, priv, rb); | ||
542 | if (ret < 0) | ||
543 | return ret; | ||
544 | |||
545 | if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) { | ||
546 | rb->count = 0; | ||
547 | vb2_ioctl_reqbufs(file, priv, rb); | ||
548 | ret = -ENOMEM; | ||
549 | } | ||
550 | |||
551 | isp->video_capture.reqbufs_count = rb->count; | ||
552 | return ret; | ||
553 | } | ||
554 | |||
555 | static const struct v4l2_ioctl_ops isp_video_ioctl_ops = { | ||
556 | .vidioc_querycap = isp_video_querycap, | ||
557 | .vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane, | ||
558 | .vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane, | ||
559 | .vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane, | ||
560 | .vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane, | ||
561 | .vidioc_reqbufs = isp_video_reqbufs, | ||
562 | .vidioc_querybuf = vb2_ioctl_querybuf, | ||
563 | .vidioc_prepare_buf = vb2_ioctl_prepare_buf, | ||
564 | .vidioc_create_bufs = vb2_ioctl_create_bufs, | ||
565 | .vidioc_qbuf = vb2_ioctl_qbuf, | ||
566 | .vidioc_dqbuf = vb2_ioctl_dqbuf, | ||
567 | .vidioc_streamon = isp_video_streamon, | ||
568 | .vidioc_streamoff = isp_video_streamoff, | ||
569 | }; | ||
570 | |||
571 | int fimc_isp_video_device_register(struct fimc_isp *isp, | ||
572 | struct v4l2_device *v4l2_dev, | ||
573 | enum v4l2_buf_type type) | ||
574 | { | ||
575 | struct vb2_queue *q = &isp->video_capture.vb_queue; | ||
576 | struct fimc_is_video *iv; | ||
577 | struct video_device *vdev; | ||
578 | int ret; | ||
579 | |||
580 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
581 | iv = &isp->video_capture; | ||
582 | else | ||
583 | return -ENOSYS; | ||
584 | |||
585 | mutex_init(&isp->video_lock); | ||
586 | INIT_LIST_HEAD(&iv->pending_buf_q); | ||
587 | INIT_LIST_HEAD(&iv->active_buf_q); | ||
588 | iv->format = fimc_isp_find_format(NULL, NULL, 0); | ||
589 | iv->pixfmt.width = IS_DEFAULT_WIDTH; | ||
590 | iv->pixfmt.height = IS_DEFAULT_HEIGHT; | ||
591 | iv->pixfmt.pixelformat = iv->format->fourcc; | ||
592 | iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB; | ||
593 | iv->reqbufs_count = 0; | ||
594 | |||
595 | memset(q, 0, sizeof(*q)); | ||
596 | q->type = type; | ||
597 | q->io_modes = VB2_MMAP | VB2_USERPTR; | ||
598 | q->ops = &isp_video_capture_qops; | ||
599 | q->mem_ops = &vb2_dma_contig_memops; | ||
600 | q->buf_struct_size = sizeof(struct isp_video_buf); | ||
601 | q->drv_priv = isp; | ||
602 | q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; | ||
603 | q->lock = &isp->video_lock; | ||
604 | |||
605 | ret = vb2_queue_init(q); | ||
606 | if (ret < 0) | ||
607 | return ret; | ||
608 | |||
609 | vdev = &iv->ve.vdev; | ||
610 | memset(vdev, 0, sizeof(*vdev)); | ||
611 | snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s", | ||
612 | type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? | ||
613 | "capture" : "output"); | ||
614 | vdev->queue = q; | ||
615 | vdev->fops = &isp_video_fops; | ||
616 | vdev->ioctl_ops = &isp_video_ioctl_ops; | ||
617 | vdev->v4l2_dev = v4l2_dev; | ||
618 | vdev->minor = -1; | ||
619 | vdev->release = video_device_release_empty; | ||
620 | vdev->lock = &isp->video_lock; | ||
621 | |||
622 | iv->pad.flags = MEDIA_PAD_FL_SINK; | ||
623 | ret = media_entity_init(&vdev->entity, 1, &iv->pad, 0); | ||
624 | if (ret < 0) | ||
625 | return ret; | ||
626 | |||
627 | video_set_drvdata(vdev, isp); | ||
628 | |||
629 | ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); | ||
630 | if (ret < 0) { | ||
631 | media_entity_cleanup(&vdev->entity); | ||
632 | return ret; | ||
633 | } | ||
634 | |||
635 | v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", | ||
636 | vdev->name, video_device_node_name(vdev)); | ||
637 | |||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | void fimc_isp_video_device_unregister(struct fimc_isp *isp, | ||
642 | enum v4l2_buf_type type) | ||
643 | { | ||
644 | struct exynos_video_entity *ve; | ||
645 | |||
646 | if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) | ||
647 | ve = &isp->video_capture.ve; | ||
648 | else | ||
649 | return; | ||
650 | |||
651 | mutex_lock(&isp->video_lock); | ||
652 | |||
653 | if (video_is_registered(&ve->vdev)) { | ||
654 | video_unregister_device(&ve->vdev); | ||
655 | media_entity_cleanup(&ve->vdev.entity); | ||
656 | ve->pipe = NULL; | ||
657 | } | ||
658 | |||
659 | mutex_unlock(&isp->video_lock); | ||
660 | } | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/exynos4-is/fimc-isp-video.h new file mode 100644 index 000000000000..98c662654bb6 --- /dev/null +++ b/drivers/media/platform/exynos4-is/fimc-isp-video.h | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Samsung Electronics Co., Ltd. | ||
5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef FIMC_ISP_VIDEO__ | ||
12 | #define FIMC_ISP_VIDEO__ | ||
13 | |||
14 | #include <media/videobuf2-core.h> | ||
15 | #include "fimc-isp.h" | ||
16 | |||
17 | #ifdef CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE | ||
18 | int fimc_isp_video_device_register(struct fimc_isp *isp, | ||
19 | struct v4l2_device *v4l2_dev, | ||
20 | enum v4l2_buf_type type); | ||
21 | |||
22 | void fimc_isp_video_device_unregister(struct fimc_isp *isp, | ||
23 | enum v4l2_buf_type type); | ||
24 | |||
25 | void fimc_isp_video_irq_handler(struct fimc_is *is); | ||
26 | #else | ||
27 | static inline void fimc_isp_video_irq_handler(struct fimc_is *is) | ||
28 | { | ||
29 | } | ||
30 | |||
31 | static inline int fimc_isp_video_device_register(struct fimc_isp *isp, | ||
32 | struct v4l2_device *v4l2_dev, | ||
33 | enum v4l2_buf_type type) | ||
34 | { | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | void fimc_isp_video_device_unregister(struct fimc_isp *isp, | ||
39 | enum v4l2_buf_type type) | ||
40 | { | ||
41 | } | ||
42 | #endif /* !CONFIG_VIDEO_EXYNOS4_ISP_DMA_CAPTURE */ | ||
43 | |||
44 | #endif /* FIMC_ISP_VIDEO__ */ | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index f3c6136aa5b4..be62d6b9ac48 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <media/v4l2-device.h> | 25 | #include <media/v4l2-device.h> |
26 | 26 | ||
27 | #include "media-dev.h" | 27 | #include "media-dev.h" |
28 | #include "fimc-isp-video.h" | ||
28 | #include "fimc-is-command.h" | 29 | #include "fimc-is-command.h" |
29 | #include "fimc-is-param.h" | 30 | #include "fimc-is-param.h" |
30 | #include "fimc-is-regs.h" | 31 | #include "fimc-is-regs.h" |
@@ -93,8 +94,8 @@ void fimc_isp_irq_handler(struct fimc_is *is) | |||
93 | is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21)); | 94 | is->i2h_cmd.args[1] = mcuctl_read(is, MCUCTL_REG_ISSR(21)); |
94 | 95 | ||
95 | fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP); | 96 | fimc_is_fw_clear_irq1(is, FIMC_IS_INT_FRAME_DONE_ISP); |
97 | fimc_isp_video_irq_handler(is); | ||
96 | 98 | ||
97 | /* TODO: Complete ISP DMA interrupt handler */ | ||
98 | wake_up(&is->irq_queue); | 99 | wake_up(&is->irq_queue); |
99 | } | 100 | } |
100 | 101 | ||
@@ -388,7 +389,33 @@ static int fimc_isp_subdev_open(struct v4l2_subdev *sd, | |||
388 | return 0; | 389 | return 0; |
389 | } | 390 | } |
390 | 391 | ||
392 | static int fimc_isp_subdev_registered(struct v4l2_subdev *sd) | ||
393 | { | ||
394 | struct fimc_isp *isp = v4l2_get_subdevdata(sd); | ||
395 | int ret; | ||
396 | |||
397 | /* Use pipeline object allocated by the media device. */ | ||
398 | isp->video_capture.ve.pipe = v4l2_get_subdev_hostdata(sd); | ||
399 | |||
400 | ret = fimc_isp_video_device_register(isp, sd->v4l2_dev, | ||
401 | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | ||
402 | if (ret < 0) | ||
403 | isp->video_capture.ve.pipe = NULL; | ||
404 | |||
405 | return ret; | ||
406 | } | ||
407 | |||
408 | static void fimc_isp_subdev_unregistered(struct v4l2_subdev *sd) | ||
409 | { | ||
410 | struct fimc_isp *isp = v4l2_get_subdevdata(sd); | ||
411 | |||
412 | fimc_isp_video_device_unregister(isp, | ||
413 | V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | ||
414 | } | ||
415 | |||
391 | static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { | 416 | static const struct v4l2_subdev_internal_ops fimc_is_subdev_internal_ops = { |
417 | .registered = fimc_isp_subdev_registered, | ||
418 | .unregistered = fimc_isp_subdev_unregistered, | ||
392 | .open = fimc_isp_subdev_open, | 419 | .open = fimc_isp_subdev_open, |
393 | }; | 420 | }; |
394 | 421 | ||
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h index 03bf95ab017b..4dc55a18d978 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/exynos4-is/fimc-isp.h | |||
@@ -35,17 +35,18 @@ extern int fimc_isp_debug; | |||
35 | #define FIMC_ISP_SINK_WIDTH_MIN (16 + 8) | 35 | #define FIMC_ISP_SINK_WIDTH_MIN (16 + 8) |
36 | #define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8) | 36 | #define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8) |
37 | #define FIMC_ISP_SOURCE_WIDTH_MIN 8 | 37 | #define FIMC_ISP_SOURCE_WIDTH_MIN 8 |
38 | #define FIMC_ISP_SOURC_HEIGHT_MIN 8 | 38 | #define FIMC_ISP_SOURCE_HEIGHT_MIN 8 |
39 | #define FIMC_ISP_CAC_MARGIN_WIDTH 16 | 39 | #define FIMC_ISP_CAC_MARGIN_WIDTH 16 |
40 | #define FIMC_ISP_CAC_MARGIN_HEIGHT 12 | 40 | #define FIMC_ISP_CAC_MARGIN_HEIGHT 12 |
41 | 41 | ||
42 | #define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16) | 42 | #define FIMC_ISP_SINK_WIDTH_MAX (4000 - 16) |
43 | #define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12) | 43 | #define FIMC_ISP_SINK_HEIGHT_MAX (4000 + 12) |
44 | #define FIMC_ISP_SOURCE_WIDTH_MAX 4000 | 44 | #define FIMC_ISP_SOURCE_WIDTH_MAX 4000 |
45 | #define FIMC_ISP_SOURC_HEIGHT_MAX 4000 | 45 | #define FIMC_ISP_SOURCE_HEIGHT_MAX 4000 |
46 | 46 | ||
47 | #define FIMC_ISP_NUM_FORMATS 3 | 47 | #define FIMC_ISP_NUM_FORMATS 3 |
48 | #define FIMC_ISP_REQ_BUFS_MIN 2 | 48 | #define FIMC_ISP_REQ_BUFS_MIN 2 |
49 | #define FIMC_ISP_REQ_BUFS_MAX 32 | ||
49 | 50 | ||
50 | #define FIMC_ISP_SD_PAD_SINK 0 | 51 | #define FIMC_ISP_SD_PAD_SINK 0 |
51 | #define FIMC_ISP_SD_PAD_SRC_FIFO 1 | 52 | #define FIMC_ISP_SD_PAD_SRC_FIFO 1 |
@@ -100,6 +101,16 @@ struct fimc_isp_ctrls { | |||
100 | struct v4l2_ctrl *colorfx; | 101 | struct v4l2_ctrl *colorfx; |
101 | }; | 102 | }; |
102 | 103 | ||
104 | struct isp_video_buf { | ||
105 | struct vb2_buffer vb; | ||
106 | dma_addr_t dma_addr[FIMC_ISP_MAX_PLANES]; | ||
107 | unsigned int index; | ||
108 | }; | ||
109 | |||
110 | #define to_isp_video_buf(_b) container_of(_b, struct isp_video_buf, vb) | ||
111 | |||
112 | #define FIMC_ISP_MAX_BUFS 4 | ||
113 | |||
103 | /** | 114 | /** |
104 | * struct fimc_is_video - fimc-is video device structure | 115 | * struct fimc_is_video - fimc-is video device structure |
105 | * @vdev: video_device structure | 116 | * @vdev: video_device structure |
@@ -114,18 +125,26 @@ struct fimc_isp_ctrls { | |||
114 | * @format: current pixel format | 125 | * @format: current pixel format |
115 | */ | 126 | */ |
116 | struct fimc_is_video { | 127 | struct fimc_is_video { |
117 | struct video_device vdev; | 128 | struct exynos_video_entity ve; |
118 | enum v4l2_buf_type type; | 129 | enum v4l2_buf_type type; |
119 | struct media_pad pad; | 130 | struct media_pad pad; |
120 | struct list_head pending_buf_q; | 131 | struct list_head pending_buf_q; |
121 | struct list_head active_buf_q; | 132 | struct list_head active_buf_q; |
122 | struct vb2_queue vb_queue; | 133 | struct vb2_queue vb_queue; |
123 | unsigned int frame_count; | ||
124 | unsigned int reqbufs_count; | 134 | unsigned int reqbufs_count; |
135 | unsigned int buf_count; | ||
136 | unsigned int buf_mask; | ||
137 | unsigned int frame_count; | ||
125 | int streaming; | 138 | int streaming; |
139 | struct isp_video_buf *buffers[FIMC_ISP_MAX_BUFS]; | ||
126 | const struct fimc_fmt *format; | 140 | const struct fimc_fmt *format; |
141 | struct v4l2_pix_format_mplane pixfmt; | ||
127 | }; | 142 | }; |
128 | 143 | ||
144 | /* struct fimc_isp:state bit definitions */ | ||
145 | #define ST_ISP_VID_CAP_BUF_PREP 0 | ||
146 | #define ST_ISP_VID_CAP_STREAMING 1 | ||
147 | |||
129 | /** | 148 | /** |
130 | * struct fimc_isp - FIMC-IS ISP data structure | 149 | * struct fimc_isp - FIMC-IS ISP data structure |
131 | * @pdev: pointer to FIMC-IS platform device | 150 | * @pdev: pointer to FIMC-IS platform device |
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 04d6ecdd314c..e62211a80f0e 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> |
@@ -25,6 +27,7 @@ | |||
25 | #include <linux/pm_runtime.h> | 27 | #include <linux/pm_runtime.h> |
26 | #include <linux/types.h> | 28 | #include <linux/types.h> |
27 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
30 | #include <media/v4l2-async.h> | ||
28 | #include <media/v4l2-ctrls.h> | 31 | #include <media/v4l2-ctrls.h> |
29 | #include <media/v4l2-of.h> | 32 | #include <media/v4l2-of.h> |
30 | #include <media/media-device.h> | 33 | #include <media/media-device.h> |
@@ -219,6 +222,7 @@ static int __fimc_pipeline_open(struct exynos_media_pipeline *ep, | |||
219 | if (ret < 0) | 222 | if (ret < 0) |
220 | return ret; | 223 | return ret; |
221 | } | 224 | } |
225 | |||
222 | ret = fimc_md_set_camclk(sd, true); | 226 | ret = fimc_md_set_camclk(sd, true); |
223 | if (ret < 0) | 227 | if (ret < 0) |
224 | goto err_wbclk; | 228 | goto err_wbclk; |
@@ -379,77 +383,18 @@ static void fimc_md_unregister_sensor(struct v4l2_subdev *sd) | |||
379 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 383 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
380 | struct i2c_adapter *adapter; | 384 | struct i2c_adapter *adapter; |
381 | 385 | ||
382 | if (!client) | 386 | if (!client || client->dev.of_node) |
383 | return; | 387 | return; |
384 | 388 | ||
385 | v4l2_device_unregister_subdev(sd); | 389 | v4l2_device_unregister_subdev(sd); |
386 | 390 | ||
387 | if (!client->dev.of_node) { | 391 | adapter = client->adapter; |
388 | adapter = client->adapter; | 392 | i2c_unregister_device(client); |
389 | i2c_unregister_device(client); | 393 | if (adapter) |
390 | if (adapter) | 394 | i2c_put_adapter(adapter); |
391 | i2c_put_adapter(adapter); | ||
392 | } | ||
393 | } | 395 | } |
394 | 396 | ||
395 | #ifdef CONFIG_OF | 397 | #ifdef CONFIG_OF |
396 | /* Register I2C client subdev associated with @node. */ | ||
397 | static int fimc_md_of_add_sensor(struct fimc_md *fmd, | ||
398 | struct device_node *node, int index) | ||
399 | { | ||
400 | struct fimc_sensor_info *si; | ||
401 | struct i2c_client *client; | ||
402 | struct v4l2_subdev *sd; | ||
403 | int ret; | ||
404 | |||
405 | if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) | ||
406 | return -EINVAL; | ||
407 | si = &fmd->sensor[index]; | ||
408 | |||
409 | client = of_find_i2c_device_by_node(node); | ||
410 | if (!client) | ||
411 | return -EPROBE_DEFER; | ||
412 | |||
413 | device_lock(&client->dev); | ||
414 | |||
415 | if (!client->dev.driver || | ||
416 | !try_module_get(client->dev.driver->owner)) { | ||
417 | ret = -EPROBE_DEFER; | ||
418 | v4l2_info(&fmd->v4l2_dev, "No driver found for %s\n", | ||
419 | node->full_name); | ||
420 | goto dev_put; | ||
421 | } | ||
422 | |||
423 | /* Enable sensor's master clock */ | ||
424 | ret = __fimc_md_set_camclk(fmd, &si->pdata, true); | ||
425 | if (ret < 0) | ||
426 | goto mod_put; | ||
427 | sd = i2c_get_clientdata(client); | ||
428 | |||
429 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); | ||
430 | __fimc_md_set_camclk(fmd, &si->pdata, false); | ||
431 | if (ret < 0) | ||
432 | goto mod_put; | ||
433 | |||
434 | v4l2_set_subdev_hostdata(sd, &si->pdata); | ||
435 | if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) | ||
436 | sd->grp_id = GRP_ID_FIMC_IS_SENSOR; | ||
437 | else | ||
438 | sd->grp_id = GRP_ID_SENSOR; | ||
439 | |||
440 | si->subdev = sd; | ||
441 | v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", | ||
442 | sd->name, fmd->num_sensors); | ||
443 | fmd->num_sensors++; | ||
444 | |||
445 | mod_put: | ||
446 | module_put(client->dev.driver->owner); | ||
447 | dev_put: | ||
448 | device_unlock(&client->dev); | ||
449 | put_device(&client->dev); | ||
450 | return ret; | ||
451 | } | ||
452 | |||
453 | /* Parse port node and register as a sub-device any sensor specified there. */ | 398 | /* Parse port node and register as a sub-device any sensor specified there. */ |
454 | static int fimc_md_parse_port_node(struct fimc_md *fmd, | 399 | static int fimc_md_parse_port_node(struct fimc_md *fmd, |
455 | struct device_node *port, | 400 | struct device_node *port, |
@@ -458,7 +403,6 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, | |||
458 | struct device_node *rem, *ep, *np; | 403 | struct device_node *rem, *ep, *np; |
459 | struct fimc_source_info *pd; | 404 | struct fimc_source_info *pd; |
460 | struct v4l2_of_endpoint endpoint; | 405 | struct v4l2_of_endpoint endpoint; |
461 | int ret; | ||
462 | u32 val; | 406 | u32 val; |
463 | 407 | ||
464 | pd = &fmd->sensor[index].pdata; | 408 | pd = &fmd->sensor[index].pdata; |
@@ -486,6 +430,8 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, | |||
486 | 430 | ||
487 | if (!of_property_read_u32(rem, "clock-frequency", &val)) | 431 | if (!of_property_read_u32(rem, "clock-frequency", &val)) |
488 | pd->clk_frequency = val; | 432 | pd->clk_frequency = val; |
433 | else | ||
434 | pd->clk_frequency = DEFAULT_SENSOR_CLK_FREQ; | ||
489 | 435 | ||
490 | if (pd->clk_frequency == 0) { | 436 | if (pd->clk_frequency == 0) { |
491 | v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", | 437 | v4l2_err(&fmd->v4l2_dev, "Wrong clock frequency at node %s\n", |
@@ -525,10 +471,17 @@ static int fimc_md_parse_port_node(struct fimc_md *fmd, | |||
525 | else | 471 | else |
526 | pd->fimc_bus_type = pd->sensor_bus_type; | 472 | pd->fimc_bus_type = pd->sensor_bus_type; |
527 | 473 | ||
528 | ret = fimc_md_of_add_sensor(fmd, rem, index); | 474 | if (WARN_ON(index >= ARRAY_SIZE(fmd->sensor))) |
529 | of_node_put(rem); | 475 | return -EINVAL; |
530 | 476 | ||
531 | return ret; | 477 | fmd->sensor[index].asd.match_type = V4L2_ASYNC_MATCH_OF; |
478 | fmd->sensor[index].asd.match.of.node = rem; | ||
479 | fmd->async_subdevs[index] = &fmd->sensor[index].asd; | ||
480 | |||
481 | fmd->num_sensors++; | ||
482 | |||
483 | of_node_put(rem); | ||
484 | return 0; | ||
532 | } | 485 | } |
533 | 486 | ||
534 | /* Register all SoC external sub-devices */ | 487 | /* Register all SoC external sub-devices */ |
@@ -732,8 +685,16 @@ static int register_csis_entity(struct fimc_md *fmd, | |||
732 | static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is) | 685 | static int register_fimc_is_entity(struct fimc_md *fmd, struct fimc_is *is) |
733 | { | 686 | { |
734 | struct v4l2_subdev *sd = &is->isp.subdev; | 687 | struct v4l2_subdev *sd = &is->isp.subdev; |
688 | struct exynos_media_pipeline *ep; | ||
735 | int ret; | 689 | int ret; |
736 | 690 | ||
691 | /* Allocate pipeline object for the ISP capture video node. */ | ||
692 | ep = fimc_md_pipeline_create(fmd); | ||
693 | if (!ep) | ||
694 | return -ENOMEM; | ||
695 | |||
696 | v4l2_set_subdev_hostdata(sd, ep); | ||
697 | |||
737 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); | 698 | ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); |
738 | if (ret) { | 699 | if (ret) { |
739 | v4l2_err(&fmd->v4l2_dev, | 700 | v4l2_err(&fmd->v4l2_dev, |
@@ -884,11 +845,13 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) | |||
884 | v4l2_device_unregister_subdev(fmd->csis[i].sd); | 845 | v4l2_device_unregister_subdev(fmd->csis[i].sd); |
885 | fmd->csis[i].sd = NULL; | 846 | fmd->csis[i].sd = NULL; |
886 | } | 847 | } |
887 | for (i = 0; i < fmd->num_sensors; i++) { | 848 | if (fmd->pdev->dev.of_node == NULL) { |
888 | if (fmd->sensor[i].subdev == NULL) | 849 | for (i = 0; i < fmd->num_sensors; i++) { |
889 | continue; | 850 | if (fmd->sensor[i].subdev == NULL) |
890 | fimc_md_unregister_sensor(fmd->sensor[i].subdev); | 851 | continue; |
891 | fmd->sensor[i].subdev = NULL; | 852 | fimc_md_unregister_sensor(fmd->sensor[i].subdev); |
853 | fmd->sensor[i].subdev = NULL; | ||
854 | } | ||
892 | } | 855 | } |
893 | 856 | ||
894 | if (fmd->fimc_is) | 857 | if (fmd->fimc_is) |
@@ -1005,16 +968,17 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) | |||
1005 | /* Create FIMC-IS links */ | 968 | /* Create FIMC-IS links */ |
1006 | static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) | 969 | static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) |
1007 | { | 970 | { |
971 | struct fimc_isp *isp = &fmd->fimc_is->isp; | ||
1008 | struct media_entity *source, *sink; | 972 | struct media_entity *source, *sink; |
1009 | int i, ret; | 973 | int i, ret; |
1010 | 974 | ||
1011 | source = &fmd->fimc_is->isp.subdev.entity; | 975 | source = &isp->subdev.entity; |
1012 | 976 | ||
1013 | for (i = 0; i < FIMC_MAX_DEVS; i++) { | 977 | for (i = 0; i < FIMC_MAX_DEVS; i++) { |
1014 | if (fmd->fimc[i] == NULL) | 978 | if (fmd->fimc[i] == NULL) |
1015 | continue; | 979 | continue; |
1016 | 980 | ||
1017 | /* Link from IS-ISP subdev to FIMC */ | 981 | /* Link from FIMC-IS-ISP subdev to FIMC */ |
1018 | sink = &fmd->fimc[i]->vid_cap.subdev.entity; | 982 | sink = &fmd->fimc[i]->vid_cap.subdev.entity; |
1019 | ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, | 983 | ret = media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_FIFO, |
1020 | sink, FIMC_SD_PAD_SINK_FIFO, 0); | 984 | sink, FIMC_SD_PAD_SINK_FIFO, 0); |
@@ -1022,7 +986,15 @@ static int __fimc_md_create_fimc_is_links(struct fimc_md *fmd) | |||
1022 | return ret; | 986 | return ret; |
1023 | } | 987 | } |
1024 | 988 | ||
1025 | return ret; | 989 | /* Link from FIMC-IS-ISP subdev to fimc-is-isp.capture video node */ |
990 | sink = &isp->video_capture.ve.vdev.entity; | ||
991 | |||
992 | /* Skip this link if the fimc-is-isp video node driver isn't built-in */ | ||
993 | if (sink->num_pads == 0) | ||
994 | return 0; | ||
995 | |||
996 | return media_entity_create_link(source, FIMC_ISP_SD_PAD_SRC_DMA, | ||
997 | sink, 0, 0); | ||
1026 | } | 998 | } |
1027 | 999 | ||
1028 | /** | 1000 | /** |
@@ -1223,6 +1195,14 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, | |||
1223 | struct fimc_camclk_info *camclk; | 1195 | struct fimc_camclk_info *camclk; |
1224 | int ret = 0; | 1196 | int ret = 0; |
1225 | 1197 | ||
1198 | /* | ||
1199 | * When device tree is used the sensor drivers are supposed to | ||
1200 | * control the clock themselves. This whole function will be | ||
1201 | * removed once S5PV210 platform is converted to the device tree. | ||
1202 | */ | ||
1203 | if (fmd->pdev->dev.of_node) | ||
1204 | return 0; | ||
1205 | |||
1226 | if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) | 1206 | if (WARN_ON(si->clk_id >= FIMC_MAX_CAMCLKS) || !fmd || !fmd->pmf) |
1227 | return -EINVAL; | 1207 | return -EINVAL; |
1228 | 1208 | ||
@@ -1277,6 +1257,14 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) | |||
1277 | struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); | 1257 | struct fimc_source_info *si = v4l2_get_subdev_hostdata(sd); |
1278 | struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); | 1258 | struct fimc_md *fmd = entity_to_fimc_mdev(&sd->entity); |
1279 | 1259 | ||
1260 | /* | ||
1261 | * If there is a clock provider registered the sensors will | ||
1262 | * handle their clock themselves, no need to control it on | ||
1263 | * the host interface side. | ||
1264 | */ | ||
1265 | if (fmd->clk_provider.num_clocks > 0) | ||
1266 | return 0; | ||
1267 | |||
1280 | return __fimc_md_set_camclk(fmd, si, on); | 1268 | return __fimc_md_set_camclk(fmd, si, on); |
1281 | } | 1269 | } |
1282 | 1270 | ||
@@ -1438,6 +1426,153 @@ static int fimc_md_get_pinctrl(struct fimc_md *fmd) | |||
1438 | return 0; | 1426 | return 0; |
1439 | } | 1427 | } |
1440 | 1428 | ||
1429 | #ifdef CONFIG_OF | ||
1430 | static int cam_clk_prepare(struct clk_hw *hw) | ||
1431 | { | ||
1432 | struct cam_clk *camclk = to_cam_clk(hw); | ||
1433 | int ret; | ||
1434 | |||
1435 | if (camclk->fmd->pmf == NULL) | ||
1436 | return -ENODEV; | ||
1437 | |||
1438 | ret = pm_runtime_get_sync(camclk->fmd->pmf); | ||
1439 | return ret < 0 ? ret : 0; | ||
1440 | } | ||
1441 | |||
1442 | static void cam_clk_unprepare(struct clk_hw *hw) | ||
1443 | { | ||
1444 | struct cam_clk *camclk = to_cam_clk(hw); | ||
1445 | |||
1446 | if (camclk->fmd->pmf == NULL) | ||
1447 | return; | ||
1448 | |||
1449 | pm_runtime_put_sync(camclk->fmd->pmf); | ||
1450 | } | ||
1451 | |||
1452 | static const struct clk_ops cam_clk_ops = { | ||
1453 | .prepare = cam_clk_prepare, | ||
1454 | .unprepare = cam_clk_unprepare, | ||
1455 | }; | ||
1456 | |||
1457 | static void fimc_md_unregister_clk_provider(struct fimc_md *fmd) | ||
1458 | { | ||
1459 | struct cam_clk_provider *cp = &fmd->clk_provider; | ||
1460 | unsigned int i; | ||
1461 | |||
1462 | if (cp->of_node) | ||
1463 | of_clk_del_provider(cp->of_node); | ||
1464 | |||
1465 | for (i = 0; i < cp->num_clocks; i++) | ||
1466 | clk_unregister(cp->clks[i]); | ||
1467 | } | ||
1468 | |||
1469 | static int fimc_md_register_clk_provider(struct fimc_md *fmd) | ||
1470 | { | ||
1471 | struct cam_clk_provider *cp = &fmd->clk_provider; | ||
1472 | struct device *dev = &fmd->pdev->dev; | ||
1473 | int i, ret; | ||
1474 | |||
1475 | for (i = 0; i < FIMC_MAX_CAMCLKS; i++) { | ||
1476 | struct cam_clk *camclk = &cp->camclk[i]; | ||
1477 | struct clk_init_data init; | ||
1478 | const char *p_name; | ||
1479 | |||
1480 | ret = of_property_read_string_index(dev->of_node, | ||
1481 | "clock-output-names", i, &init.name); | ||
1482 | if (ret < 0) | ||
1483 | break; | ||
1484 | |||
1485 | p_name = __clk_get_name(fmd->camclk[i].clock); | ||
1486 | |||
1487 | /* It's safe since clk_register() will duplicate the string. */ | ||
1488 | init.parent_names = &p_name; | ||
1489 | init.num_parents = 1; | ||
1490 | init.ops = &cam_clk_ops; | ||
1491 | init.flags = CLK_SET_RATE_PARENT; | ||
1492 | camclk->hw.init = &init; | ||
1493 | camclk->fmd = fmd; | ||
1494 | |||
1495 | cp->clks[i] = clk_register(NULL, &camclk->hw); | ||
1496 | if (IS_ERR(cp->clks[i])) { | ||
1497 | dev_err(dev, "failed to register clock: %s (%ld)\n", | ||
1498 | init.name, PTR_ERR(cp->clks[i])); | ||
1499 | ret = PTR_ERR(cp->clks[i]); | ||
1500 | goto err; | ||
1501 | } | ||
1502 | cp->num_clocks++; | ||
1503 | } | ||
1504 | |||
1505 | if (cp->num_clocks == 0) { | ||
1506 | dev_warn(dev, "clk provider not registered\n"); | ||
1507 | return 0; | ||
1508 | } | ||
1509 | |||
1510 | cp->clk_data.clks = cp->clks; | ||
1511 | cp->clk_data.clk_num = cp->num_clocks; | ||
1512 | cp->of_node = dev->of_node; | ||
1513 | ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, | ||
1514 | &cp->clk_data); | ||
1515 | if (ret == 0) | ||
1516 | return 0; | ||
1517 | err: | ||
1518 | fimc_md_unregister_clk_provider(fmd); | ||
1519 | return ret; | ||
1520 | } | ||
1521 | #else | ||
1522 | #define fimc_md_register_clk_provider(fmd) (0) | ||
1523 | #define fimc_md_unregister_clk_provider(fmd) (0) | ||
1524 | #endif | ||
1525 | |||
1526 | static int subdev_notifier_bound(struct v4l2_async_notifier *notifier, | ||
1527 | struct v4l2_subdev *subdev, | ||
1528 | struct v4l2_async_subdev *asd) | ||
1529 | { | ||
1530 | struct fimc_md *fmd = notifier_to_fimc_md(notifier); | ||
1531 | struct fimc_sensor_info *si = NULL; | ||
1532 | int i; | ||
1533 | |||
1534 | /* Find platform data for this sensor subdev */ | ||
1535 | for (i = 0; i < ARRAY_SIZE(fmd->sensor); i++) | ||
1536 | if (fmd->sensor[i].asd.match.of.node == subdev->dev->of_node) | ||
1537 | si = &fmd->sensor[i]; | ||
1538 | |||
1539 | if (si == NULL) | ||
1540 | return -EINVAL; | ||
1541 | |||
1542 | v4l2_set_subdev_hostdata(subdev, &si->pdata); | ||
1543 | |||
1544 | if (si->pdata.fimc_bus_type == FIMC_BUS_TYPE_ISP_WRITEBACK) | ||
1545 | subdev->grp_id = GRP_ID_FIMC_IS_SENSOR; | ||
1546 | else | ||
1547 | subdev->grp_id = GRP_ID_SENSOR; | ||
1548 | |||
1549 | si->subdev = subdev; | ||
1550 | |||
1551 | v4l2_info(&fmd->v4l2_dev, "Registered sensor subdevice: %s (%d)\n", | ||
1552 | subdev->name, fmd->num_sensors); | ||
1553 | |||
1554 | fmd->num_sensors++; | ||
1555 | |||
1556 | return 0; | ||
1557 | } | ||
1558 | |||
1559 | static int subdev_notifier_complete(struct v4l2_async_notifier *notifier) | ||
1560 | { | ||
1561 | struct fimc_md *fmd = notifier_to_fimc_md(notifier); | ||
1562 | int ret; | ||
1563 | |||
1564 | mutex_lock(&fmd->media_dev.graph_mutex); | ||
1565 | |||
1566 | ret = fimc_md_create_links(fmd); | ||
1567 | if (ret < 0) | ||
1568 | goto unlock; | ||
1569 | |||
1570 | ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); | ||
1571 | unlock: | ||
1572 | mutex_unlock(&fmd->media_dev.graph_mutex); | ||
1573 | return ret; | ||
1574 | } | ||
1575 | |||
1441 | static int fimc_md_probe(struct platform_device *pdev) | 1576 | static int fimc_md_probe(struct platform_device *pdev) |
1442 | { | 1577 | { |
1443 | struct device *dev = &pdev->dev; | 1578 | struct device *dev = &pdev->dev; |
@@ -1470,63 +1605,91 @@ static int fimc_md_probe(struct platform_device *pdev) | |||
1470 | v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); | 1605 | v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret); |
1471 | return ret; | 1606 | return ret; |
1472 | } | 1607 | } |
1608 | |||
1473 | ret = media_device_register(&fmd->media_dev); | 1609 | ret = media_device_register(&fmd->media_dev); |
1474 | if (ret < 0) { | 1610 | if (ret < 0) { |
1475 | v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); | 1611 | v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); |
1476 | goto err_md; | 1612 | goto err_v4l2_dev; |
1477 | } | 1613 | } |
1614 | |||
1478 | ret = fimc_md_get_clocks(fmd); | 1615 | ret = fimc_md_get_clocks(fmd); |
1479 | if (ret) | 1616 | if (ret) |
1480 | goto err_clk; | 1617 | goto err_md; |
1481 | 1618 | ||
1482 | fmd->user_subdev_api = (dev->of_node != NULL); | 1619 | fmd->user_subdev_api = (dev->of_node != NULL); |
1483 | 1620 | ||
1484 | /* Protect the media graph while we're registering entities */ | ||
1485 | mutex_lock(&fmd->media_dev.graph_mutex); | ||
1486 | |||
1487 | ret = fimc_md_get_pinctrl(fmd); | 1621 | ret = fimc_md_get_pinctrl(fmd); |
1488 | if (ret < 0) { | 1622 | if (ret < 0) { |
1489 | if (ret != EPROBE_DEFER) | 1623 | if (ret != EPROBE_DEFER) |
1490 | dev_err(dev, "Failed to get pinctrl: %d\n", ret); | 1624 | dev_err(dev, "Failed to get pinctrl: %d\n", ret); |
1491 | goto err_unlock; | 1625 | goto err_clk; |
1492 | } | 1626 | } |
1493 | 1627 | ||
1628 | platform_set_drvdata(pdev, fmd); | ||
1629 | |||
1630 | /* Protect the media graph while we're registering entities */ | ||
1631 | mutex_lock(&fmd->media_dev.graph_mutex); | ||
1632 | |||
1494 | if (dev->of_node) | 1633 | if (dev->of_node) |
1495 | ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); | 1634 | ret = fimc_md_register_of_platform_entities(fmd, dev->of_node); |
1496 | else | 1635 | else |
1497 | ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, | 1636 | ret = bus_for_each_dev(&platform_bus_type, NULL, fmd, |
1498 | fimc_md_pdev_match); | 1637 | fimc_md_pdev_match); |
1499 | if (ret) | 1638 | if (ret) { |
1500 | goto err_unlock; | 1639 | mutex_unlock(&fmd->media_dev.graph_mutex); |
1640 | goto err_clk; | ||
1641 | } | ||
1501 | 1642 | ||
1502 | if (dev->platform_data || dev->of_node) { | 1643 | if (dev->platform_data || dev->of_node) { |
1503 | ret = fimc_md_register_sensor_entities(fmd); | 1644 | ret = fimc_md_register_sensor_entities(fmd); |
1504 | if (ret) | 1645 | if (ret) { |
1505 | goto err_unlock; | 1646 | mutex_unlock(&fmd->media_dev.graph_mutex); |
1647 | goto err_m_ent; | ||
1648 | } | ||
1506 | } | 1649 | } |
1507 | 1650 | ||
1508 | ret = fimc_md_create_links(fmd); | 1651 | mutex_unlock(&fmd->media_dev.graph_mutex); |
1509 | if (ret) | ||
1510 | goto err_unlock; | ||
1511 | ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); | ||
1512 | if (ret) | ||
1513 | goto err_unlock; | ||
1514 | 1652 | ||
1515 | ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); | 1653 | ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); |
1516 | if (ret) | 1654 | if (ret) |
1517 | goto err_unlock; | 1655 | goto err_m_ent; |
1656 | /* | ||
1657 | * FIMC platform devices need to be registered before the sclk_cam | ||
1658 | * clocks provider, as one of these devices needs to be activated | ||
1659 | * to enable the clock. | ||
1660 | */ | ||
1661 | ret = fimc_md_register_clk_provider(fmd); | ||
1662 | if (ret < 0) { | ||
1663 | v4l2_err(v4l2_dev, "clock provider registration failed\n"); | ||
1664 | goto err_attr; | ||
1665 | } | ||
1666 | |||
1667 | if (fmd->num_sensors > 0) { | ||
1668 | fmd->subdev_notifier.subdevs = fmd->async_subdevs; | ||
1669 | fmd->subdev_notifier.num_subdevs = fmd->num_sensors; | ||
1670 | fmd->subdev_notifier.bound = subdev_notifier_bound; | ||
1671 | fmd->subdev_notifier.complete = subdev_notifier_complete; | ||
1672 | fmd->num_sensors = 0; | ||
1673 | |||
1674 | ret = v4l2_async_notifier_register(&fmd->v4l2_dev, | ||
1675 | &fmd->subdev_notifier); | ||
1676 | if (ret) | ||
1677 | goto err_clk_p; | ||
1678 | } | ||
1518 | 1679 | ||
1519 | platform_set_drvdata(pdev, fmd); | ||
1520 | mutex_unlock(&fmd->media_dev.graph_mutex); | ||
1521 | return 0; | 1680 | return 0; |
1522 | 1681 | ||
1523 | err_unlock: | 1682 | err_clk_p: |
1524 | mutex_unlock(&fmd->media_dev.graph_mutex); | 1683 | fimc_md_unregister_clk_provider(fmd); |
1684 | err_attr: | ||
1685 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); | ||
1525 | err_clk: | 1686 | err_clk: |
1526 | fimc_md_put_clocks(fmd); | 1687 | fimc_md_put_clocks(fmd); |
1688 | err_m_ent: | ||
1527 | fimc_md_unregister_entities(fmd); | 1689 | fimc_md_unregister_entities(fmd); |
1528 | media_device_unregister(&fmd->media_dev); | ||
1529 | err_md: | 1690 | err_md: |
1691 | media_device_unregister(&fmd->media_dev); | ||
1692 | err_v4l2_dev: | ||
1530 | v4l2_device_unregister(&fmd->v4l2_dev); | 1693 | v4l2_device_unregister(&fmd->v4l2_dev); |
1531 | return ret; | 1694 | return ret; |
1532 | } | 1695 | } |
@@ -1538,12 +1701,16 @@ static int fimc_md_remove(struct platform_device *pdev) | |||
1538 | if (!fmd) | 1701 | if (!fmd) |
1539 | return 0; | 1702 | return 0; |
1540 | 1703 | ||
1704 | fimc_md_unregister_clk_provider(fmd); | ||
1705 | v4l2_async_notifier_unregister(&fmd->subdev_notifier); | ||
1706 | |||
1541 | v4l2_device_unregister(&fmd->v4l2_dev); | 1707 | v4l2_device_unregister(&fmd->v4l2_dev); |
1542 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); | 1708 | device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); |
1543 | fimc_md_unregister_entities(fmd); | 1709 | fimc_md_unregister_entities(fmd); |
1544 | fimc_md_pipelines_free(fmd); | 1710 | fimc_md_pipelines_free(fmd); |
1545 | media_device_unregister(&fmd->media_dev); | 1711 | media_device_unregister(&fmd->media_dev); |
1546 | fimc_md_put_clocks(fmd); | 1712 | fimc_md_put_clocks(fmd); |
1713 | |||
1547 | return 0; | 1714 | return 0; |
1548 | } | 1715 | } |
1549 | 1716 | ||
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 62599fd7756f..ee1e2519f728 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> |
@@ -31,8 +32,9 @@ | |||
31 | 32 | ||
32 | #define PINCTRL_STATE_IDLE "idle" | 33 | #define PINCTRL_STATE_IDLE "idle" |
33 | 34 | ||
34 | #define FIMC_MAX_SENSORS 8 | 35 | #define FIMC_MAX_SENSORS 4 |
35 | #define FIMC_MAX_CAMCLKS 2 | 36 | #define FIMC_MAX_CAMCLKS 2 |
37 | #define DEFAULT_SENSOR_CLK_FREQ 24000000U | ||
36 | 38 | ||
37 | /* LCD/ISP Writeback clocks (PIXELASYNCMx) */ | 39 | /* LCD/ISP Writeback clocks (PIXELASYNCMx) */ |
38 | enum { | 40 | enum { |
@@ -78,6 +80,7 @@ struct fimc_camclk_info { | |||
78 | /** | 80 | /** |
79 | * struct fimc_sensor_info - image data source subdev information | 81 | * struct fimc_sensor_info - image data source subdev information |
80 | * @pdata: sensor's atrributes passed as media device's platform data | 82 | * @pdata: sensor's atrributes passed as media device's platform data |
83 | * @asd: asynchronous subdev registration data structure | ||
81 | * @subdev: image sensor v4l2 subdev | 84 | * @subdev: image sensor v4l2 subdev |
82 | * @host: fimc device the sensor is currently linked to | 85 | * @host: fimc device the sensor is currently linked to |
83 | * | 86 | * |
@@ -85,10 +88,17 @@ struct fimc_camclk_info { | |||
85 | */ | 88 | */ |
86 | struct fimc_sensor_info { | 89 | struct fimc_sensor_info { |
87 | struct fimc_source_info pdata; | 90 | struct fimc_source_info pdata; |
91 | struct v4l2_async_subdev asd; | ||
88 | struct v4l2_subdev *subdev; | 92 | struct v4l2_subdev *subdev; |
89 | struct fimc_dev *host; | 93 | struct fimc_dev *host; |
90 | }; | 94 | }; |
91 | 95 | ||
96 | struct cam_clk { | ||
97 | struct clk_hw hw; | ||
98 | struct fimc_md *fmd; | ||
99 | }; | ||
100 | #define to_cam_clk(_hw) container_of(_hw, struct cam_clk, hw) | ||
101 | |||
92 | /** | 102 | /** |
93 | * struct fimc_md - fimc media device information | 103 | * struct fimc_md - fimc media device information |
94 | * @csis: MIPI CSIS subdevs data | 104 | * @csis: MIPI CSIS subdevs data |
@@ -105,6 +115,7 @@ struct fimc_sensor_info { | |||
105 | * @pinctrl: camera port pinctrl handle | 115 | * @pinctrl: camera port pinctrl handle |
106 | * @state_default: pinctrl default state handle | 116 | * @state_default: pinctrl default state handle |
107 | * @state_idle: pinctrl idle state handle | 117 | * @state_idle: pinctrl idle state handle |
118 | * @cam_clk_provider: CAMCLK clock provider structure | ||
108 | * @user_subdev_api: true if subdevs are not configured by the host driver | 119 | * @user_subdev_api: true if subdevs are not configured by the host driver |
109 | * @slock: spinlock protecting @sensor array | 120 | * @slock: spinlock protecting @sensor array |
110 | */ | 121 | */ |
@@ -122,13 +133,25 @@ struct fimc_md { | |||
122 | struct media_device media_dev; | 133 | struct media_device media_dev; |
123 | struct v4l2_device v4l2_dev; | 134 | struct v4l2_device v4l2_dev; |
124 | struct platform_device *pdev; | 135 | struct platform_device *pdev; |
136 | |||
125 | struct fimc_pinctrl { | 137 | struct fimc_pinctrl { |
126 | struct pinctrl *pinctrl; | 138 | struct pinctrl *pinctrl; |
127 | struct pinctrl_state *state_default; | 139 | struct pinctrl_state *state_default; |
128 | struct pinctrl_state *state_idle; | 140 | struct pinctrl_state *state_idle; |
129 | } pinctl; | 141 | } pinctl; |
130 | bool user_subdev_api; | ||
131 | 142 | ||
143 | struct cam_clk_provider { | ||
144 | struct clk *clks[FIMC_MAX_CAMCLKS]; | ||
145 | struct clk_onecell_data clk_data; | ||
146 | struct device_node *of_node; | ||
147 | struct cam_clk camclk[FIMC_MAX_CAMCLKS]; | ||
148 | int num_clocks; | ||
149 | } clk_provider; | ||
150 | |||
151 | struct v4l2_async_notifier subdev_notifier; | ||
152 | struct v4l2_async_subdev *async_subdevs[FIMC_MAX_SENSORS]; | ||
153 | |||
154 | bool user_subdev_api; | ||
132 | spinlock_t slock; | 155 | spinlock_t slock; |
133 | struct list_head pipelines; | 156 | struct list_head pipelines; |
134 | }; | 157 | }; |
@@ -145,6 +168,11 @@ static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) | |||
145 | container_of(me->parent, struct fimc_md, media_dev); | 168 | container_of(me->parent, struct fimc_md, media_dev); |
146 | } | 169 | } |
147 | 170 | ||
171 | static inline struct fimc_md *notifier_to_fimc_md(struct v4l2_async_notifier *n) | ||
172 | { | ||
173 | return container_of(n, struct fimc_md, subdev_notifier); | ||
174 | } | ||
175 | |||
148 | static inline void fimc_md_graph_lock(struct exynos_video_entity *ve) | 176 | static inline void fimc_md_graph_lock(struct exynos_video_entity *ve) |
149 | { | 177 | { |
150 | mutex_lock(&ve->vdev.entity.parent->graph_mutex); | 178 | mutex_lock(&ve->vdev.entity.parent->graph_mutex); |
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/s5p-jpeg/jpeg-regs.h index 33f2c7374cfd..57fb05bb8c77 100644 --- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h +++ b/drivers/media/platform/s5p-jpeg/jpeg-regs.h | |||
@@ -210,19 +210,19 @@ | |||
210 | 210 | ||
211 | /* JPEG CNTL Register bit */ | 211 | /* JPEG CNTL Register bit */ |
212 | #define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0) | 212 | #define EXYNOS4_ENC_DEC_MODE_MASK (0xfffffffc << 0) |
213 | #define EXYNOS4_DEC_MODE (1 << 0) | 213 | #define EXYNOS4_DEC_MODE (1 << 0) |
214 | #define EXYNOS4_ENC_MODE (1 << 1) | 214 | #define EXYNOS4_ENC_MODE (1 << 1) |
215 | #define EXYNOS4_AUTO_RST_MARKER (1 << 2) | 215 | #define EXYNOS4_AUTO_RST_MARKER (1 << 2) |
216 | #define EXYNOS4_RST_INTERVAL_SHIFT 3 | 216 | #define EXYNOS4_RST_INTERVAL_SHIFT 3 |
217 | #define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \ | 217 | #define EXYNOS4_RST_INTERVAL(x) (((x) & 0xffff) \ |
218 | << EXYNOS4_RST_INTERVAL_SHIFT) | 218 | << EXYNOS4_RST_INTERVAL_SHIFT) |
219 | #define EXYNOS4_HUF_TBL_EN (1 << 19) | 219 | #define EXYNOS4_HUF_TBL_EN (1 << 19) |
220 | #define EXYNOS4_HOR_SCALING_SHIFT 20 | 220 | #define EXYNOS4_HOR_SCALING_SHIFT 20 |
221 | #define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT) | 221 | #define EXYNOS4_HOR_SCALING_MASK (3 << EXYNOS4_HOR_SCALING_SHIFT) |
222 | #define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \ | 222 | #define EXYNOS4_HOR_SCALING(x) (((x) & 0x3) \ |
223 | << EXYNOS4_HOR_SCALING_SHIFT) | 223 | << EXYNOS4_HOR_SCALING_SHIFT) |
224 | #define EXYNOS4_VER_SCALING_SHIFT 22 | 224 | #define EXYNOS4_VER_SCALING_SHIFT 22 |
225 | #define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT) | 225 | #define EXYNOS4_VER_SCALING_MASK (3 << EXYNOS4_VER_SCALING_SHIFT) |
226 | #define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \ | 226 | #define EXYNOS4_VER_SCALING(x) (((x) & 0x3) \ |
227 | << EXYNOS4_VER_SCALING_SHIFT) | 227 | << EXYNOS4_VER_SCALING_SHIFT) |
228 | #define EXYNOS4_PADDING (1 << 27) | 228 | #define EXYNOS4_PADDING (1 << 27) |
@@ -238,8 +238,8 @@ | |||
238 | #define EXYNOS4_FRAME_ERR_EN (1 << 4) | 238 | #define EXYNOS4_FRAME_ERR_EN (1 << 4) |
239 | #define EXYNOS4_INT_EN_ALL (0x1f << 0) | 239 | #define EXYNOS4_INT_EN_ALL (0x1f << 0) |
240 | 240 | ||
241 | #define EXYNOS4_MOD_REG_PROC_ENC (0 << 3) | 241 | #define EXYNOS4_MOD_REG_PROC_ENC (0 << 3) |
242 | #define EXYNOS4_MOD_REG_PROC_DEC (1 << 3) | 242 | #define EXYNOS4_MOD_REG_PROC_DEC (1 << 3) |
243 | 243 | ||
244 | #define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0) | 244 | #define EXYNOS4_MOD_REG_SUBSAMPLE_444 (0 << 0) |
245 | #define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0) | 245 | #define EXYNOS4_MOD_REG_SUBSAMPLE_422 (1 << 0) |
@@ -270,7 +270,7 @@ | |||
270 | #define EXYNOS4_DEC_YUV_420_IMG (4 << 0) | 270 | #define EXYNOS4_DEC_YUV_420_IMG (4 << 0) |
271 | 271 | ||
272 | #define EXYNOS4_GRAY_IMG_IP_SHIFT 3 | 272 | #define EXYNOS4_GRAY_IMG_IP_SHIFT 3 |
273 | #define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT) | 273 | #define EXYNOS4_GRAY_IMG_IP_MASK (7 << EXYNOS4_GRAY_IMG_IP_SHIFT) |
274 | #define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT) | 274 | #define EXYNOS4_GRAY_IMG_IP (4 << EXYNOS4_GRAY_IMG_IP_SHIFT) |
275 | 275 | ||
276 | #define EXYNOS4_RGB_IP_SHIFT 6 | 276 | #define EXYNOS4_RGB_IP_SHIFT 6 |
@@ -278,18 +278,18 @@ | |||
278 | #define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT) | 278 | #define EXYNOS4_RGB_IP_RGB_16BIT_IMG (4 << EXYNOS4_RGB_IP_SHIFT) |
279 | #define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT) | 279 | #define EXYNOS4_RGB_IP_RGB_32BIT_IMG (5 << EXYNOS4_RGB_IP_SHIFT) |
280 | 280 | ||
281 | #define EXYNOS4_YUV_444_IP_SHIFT 9 | 281 | #define EXYNOS4_YUV_444_IP_SHIFT 9 |
282 | #define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT) | 282 | #define EXYNOS4_YUV_444_IP_MASK (7 << EXYNOS4_YUV_444_IP_SHIFT) |
283 | #define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT) | 283 | #define EXYNOS4_YUV_444_IP_YUV_444_2P_IMG (4 << EXYNOS4_YUV_444_IP_SHIFT) |
284 | #define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT) | 284 | #define EXYNOS4_YUV_444_IP_YUV_444_3P_IMG (5 << EXYNOS4_YUV_444_IP_SHIFT) |
285 | 285 | ||
286 | #define EXYNOS4_YUV_422_IP_SHIFT 12 | 286 | #define EXYNOS4_YUV_422_IP_SHIFT 12 |
287 | #define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT) | 287 | #define EXYNOS4_YUV_422_IP_MASK (7 << EXYNOS4_YUV_422_IP_SHIFT) |
288 | #define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT) | 288 | #define EXYNOS4_YUV_422_IP_YUV_422_1P_IMG (4 << EXYNOS4_YUV_422_IP_SHIFT) |
289 | #define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT) | 289 | #define EXYNOS4_YUV_422_IP_YUV_422_2P_IMG (5 << EXYNOS4_YUV_422_IP_SHIFT) |
290 | #define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT) | 290 | #define EXYNOS4_YUV_422_IP_YUV_422_3P_IMG (6 << EXYNOS4_YUV_422_IP_SHIFT) |
291 | 291 | ||
292 | #define EXYNOS4_YUV_420_IP_SHIFT 15 | 292 | #define EXYNOS4_YUV_420_IP_SHIFT 15 |
293 | #define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT) | 293 | #define EXYNOS4_YUV_420_IP_MASK (7 << EXYNOS4_YUV_420_IP_SHIFT) |
294 | #define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT) | 294 | #define EXYNOS4_YUV_420_IP_YUV_420_2P_IMG (4 << EXYNOS4_YUV_420_IP_SHIFT) |
295 | #define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT) | 295 | #define EXYNOS4_YUV_420_IP_YUV_420_3P_IMG (5 << EXYNOS4_YUV_420_IP_SHIFT) |
@@ -303,8 +303,8 @@ | |||
303 | 303 | ||
304 | #define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03 | 304 | #define EXYNOS4_JPEG_DECODED_IMG_FMT_MASK 0x03 |
305 | 305 | ||
306 | #define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26) | 306 | #define EXYNOS4_SWAP_CHROMA_CRCB (1 << 26) |
307 | #define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26) | 307 | #define EXYNOS4_SWAP_CHROMA_CBCR (0 << 26) |
308 | 308 | ||
309 | /* JPEG HUFF count Register bit */ | 309 | /* JPEG HUFF count Register bit */ |
310 | #define EXYNOS4_HUFF_COUNT_MASK 0xffff | 310 | #define EXYNOS4_HUFF_COUNT_MASK 0xffff |