diff options
Diffstat (limited to 'drivers/media/platform/s5p-fimc/mipi-csis.c')
-rw-r--r-- | drivers/media/platform/s5p-fimc/mipi-csis.c | 75 |
1 files changed, 62 insertions, 13 deletions
diff --git a/drivers/media/platform/s5p-fimc/mipi-csis.c b/drivers/media/platform/s5p-fimc/mipi-csis.c index e92236ac5cfe..4c961b1b68e6 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver | 2 | * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver |
3 | * | 3 | * |
4 | * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. | 4 | * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. |
5 | * Sylwester Nawrocki, <s.nawrocki@samsung.com> | 5 | * Sylwester Nawrocki <s.nawrocki@samsung.com> |
6 | * | 6 | * |
7 | * 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 |
8 | * 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 |
@@ -98,6 +98,11 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); | |||
98 | #define CSIS_MAX_PIX_WIDTH 0xffff | 98 | #define CSIS_MAX_PIX_WIDTH 0xffff |
99 | #define CSIS_MAX_PIX_HEIGHT 0xffff | 99 | #define CSIS_MAX_PIX_HEIGHT 0xffff |
100 | 100 | ||
101 | /* Non-image packet data buffers */ | ||
102 | #define S5PCSIS_PKTDATA_ODD 0x2000 | ||
103 | #define S5PCSIS_PKTDATA_EVEN 0x3000 | ||
104 | #define S5PCSIS_PKTDATA_SIZE SZ_4K | ||
105 | |||
101 | enum { | 106 | enum { |
102 | CSIS_CLK_MUX, | 107 | CSIS_CLK_MUX, |
103 | CSIS_CLK_GATE, | 108 | CSIS_CLK_GATE, |
@@ -110,8 +115,8 @@ static char *csi_clock_name[] = { | |||
110 | #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) | 115 | #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) |
111 | 116 | ||
112 | static const char * const csis_supply_name[] = { | 117 | static const char * const csis_supply_name[] = { |
113 | "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */ | 118 | "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ |
114 | "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */ | 119 | "vddio", /* CSIS I/O and PLL (1.8V) supply */ |
115 | }; | 120 | }; |
116 | #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name) | 121 | #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name) |
117 | 122 | ||
@@ -144,12 +149,18 @@ static const struct s5pcsis_event s5pcsis_events[] = { | |||
144 | }; | 149 | }; |
145 | #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events) | 150 | #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events) |
146 | 151 | ||
152 | struct csis_pktbuf { | ||
153 | u32 *data; | ||
154 | unsigned int len; | ||
155 | }; | ||
156 | |||
147 | /** | 157 | /** |
148 | * struct csis_state - the driver's internal state data structure | 158 | * struct csis_state - the driver's internal state data structure |
149 | * @lock: mutex serializing the subdev and power management operations, | 159 | * @lock: mutex serializing the subdev and power management operations, |
150 | * protecting @format and @flags members | 160 | * protecting @format and @flags members |
151 | * @pads: CSIS pads array | 161 | * @pads: CSIS pads array |
152 | * @sd: v4l2_subdev associated with CSIS device instance | 162 | * @sd: v4l2_subdev associated with CSIS device instance |
163 | * @index: the hardware instance index | ||
153 | * @pdev: CSIS platform device | 164 | * @pdev: CSIS platform device |
154 | * @regs: mmaped I/O registers memory | 165 | * @regs: mmaped I/O registers memory |
155 | * @supplies: CSIS regulator supplies | 166 | * @supplies: CSIS regulator supplies |
@@ -159,12 +170,14 @@ static const struct s5pcsis_event s5pcsis_events[] = { | |||
159 | * @csis_fmt: current CSIS pixel format | 170 | * @csis_fmt: current CSIS pixel format |
160 | * @format: common media bus format for the source and sink pad | 171 | * @format: common media bus format for the source and sink pad |
161 | * @slock: spinlock protecting structure members below | 172 | * @slock: spinlock protecting structure members below |
173 | * @pkt_buf: the frame embedded (non-image) data buffer | ||
162 | * @events: MIPI-CSIS event (error) counters | 174 | * @events: MIPI-CSIS event (error) counters |
163 | */ | 175 | */ |
164 | struct csis_state { | 176 | struct csis_state { |
165 | struct mutex lock; | 177 | struct mutex lock; |
166 | struct media_pad pads[CSIS_PADS_NUM]; | 178 | struct media_pad pads[CSIS_PADS_NUM]; |
167 | struct v4l2_subdev sd; | 179 | struct v4l2_subdev sd; |
180 | u8 index; | ||
168 | struct platform_device *pdev; | 181 | struct platform_device *pdev; |
169 | void __iomem *regs; | 182 | void __iomem *regs; |
170 | struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; | 183 | struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; |
@@ -175,6 +188,7 @@ struct csis_state { | |||
175 | struct v4l2_mbus_framefmt format; | 188 | struct v4l2_mbus_framefmt format; |
176 | 189 | ||
177 | struct spinlock slock; | 190 | struct spinlock slock; |
191 | struct csis_pktbuf pkt_buf; | ||
178 | struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; | 192 | struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; |
179 | }; | 193 | }; |
180 | 194 | ||
@@ -202,7 +216,11 @@ static const struct csis_pix_format s5pcsis_formats[] = { | |||
202 | .code = V4L2_MBUS_FMT_JPEG_1X8, | 216 | .code = V4L2_MBUS_FMT_JPEG_1X8, |
203 | .fmt_reg = S5PCSIS_CFG_FMT_USER(1), | 217 | .fmt_reg = S5PCSIS_CFG_FMT_USER(1), |
204 | .data_alignment = 32, | 218 | .data_alignment = 32, |
205 | }, | 219 | }, { |
220 | .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, | ||
221 | .fmt_reg = S5PCSIS_CFG_FMT_USER(1), | ||
222 | .data_alignment = 32, | ||
223 | } | ||
206 | }; | 224 | }; |
207 | 225 | ||
208 | #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r) | 226 | #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r) |
@@ -266,7 +284,7 @@ static void __s5pcsis_set_format(struct csis_state *state) | |||
266 | struct v4l2_mbus_framefmt *mf = &state->format; | 284 | struct v4l2_mbus_framefmt *mf = &state->format; |
267 | u32 val; | 285 | u32 val; |
268 | 286 | ||
269 | v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n", | 287 | v4l2_dbg(1, debug, &state->sd, "fmt: %#x, %d x %d\n", |
270 | mf->code, mf->width, mf->height); | 288 | mf->code, mf->width, mf->height); |
271 | 289 | ||
272 | /* Color format */ | 290 | /* Color format */ |
@@ -304,8 +322,10 @@ static void s5pcsis_set_params(struct csis_state *state) | |||
304 | val |= S5PCSIS_CTRL_ALIGN_32BIT; | 322 | val |= S5PCSIS_CTRL_ALIGN_32BIT; |
305 | else /* 24-bits */ | 323 | else /* 24-bits */ |
306 | val &= ~S5PCSIS_CTRL_ALIGN_32BIT; | 324 | val &= ~S5PCSIS_CTRL_ALIGN_32BIT; |
307 | /* Not using external clock. */ | 325 | |
308 | val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; | 326 | val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; |
327 | if (pdata->wclk_source) | ||
328 | val |= S5PCSIS_CTRL_WCLK_EXTCLK; | ||
309 | s5pcsis_write(state, S5PCSIS_CTRL, val); | 329 | s5pcsis_write(state, S5PCSIS_CTRL, val); |
310 | 330 | ||
311 | /* Update the shadow register. */ | 331 | /* Update the shadow register. */ |
@@ -529,6 +549,22 @@ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, | |||
529 | return 0; | 549 | return 0; |
530 | } | 550 | } |
531 | 551 | ||
552 | static int s5pcsis_s_rx_buffer(struct v4l2_subdev *sd, void *buf, | ||
553 | unsigned int *size) | ||
554 | { | ||
555 | struct csis_state *state = sd_to_csis_state(sd); | ||
556 | unsigned long flags; | ||
557 | |||
558 | *size = min_t(unsigned int, *size, S5PCSIS_PKTDATA_SIZE); | ||
559 | |||
560 | spin_lock_irqsave(&state->slock, flags); | ||
561 | state->pkt_buf.data = buf; | ||
562 | state->pkt_buf.len = *size; | ||
563 | spin_unlock_irqrestore(&state->slock, flags); | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | |||
532 | static int s5pcsis_log_status(struct v4l2_subdev *sd) | 568 | static int s5pcsis_log_status(struct v4l2_subdev *sd) |
533 | { | 569 | { |
534 | struct csis_state *state = sd_to_csis_state(sd); | 570 | struct csis_state *state = sd_to_csis_state(sd); |
@@ -566,6 +602,7 @@ static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { | |||
566 | }; | 602 | }; |
567 | 603 | ||
568 | static struct v4l2_subdev_video_ops s5pcsis_video_ops = { | 604 | static struct v4l2_subdev_video_ops s5pcsis_video_ops = { |
605 | .s_rx_buffer = s5pcsis_s_rx_buffer, | ||
569 | .s_stream = s5pcsis_s_stream, | 606 | .s_stream = s5pcsis_s_stream, |
570 | }; | 607 | }; |
571 | 608 | ||
@@ -578,13 +615,26 @@ static struct v4l2_subdev_ops s5pcsis_subdev_ops = { | |||
578 | static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) | 615 | static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) |
579 | { | 616 | { |
580 | struct csis_state *state = dev_id; | 617 | struct csis_state *state = dev_id; |
618 | struct csis_pktbuf *pktbuf = &state->pkt_buf; | ||
581 | unsigned long flags; | 619 | unsigned long flags; |
582 | u32 status; | 620 | u32 status; |
583 | 621 | ||
584 | status = s5pcsis_read(state, S5PCSIS_INTSRC); | 622 | status = s5pcsis_read(state, S5PCSIS_INTSRC); |
585 | |||
586 | spin_lock_irqsave(&state->slock, flags); | 623 | spin_lock_irqsave(&state->slock, flags); |
587 | 624 | ||
625 | if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) { | ||
626 | u32 offset; | ||
627 | |||
628 | if (status & S5PCSIS_INTSRC_EVEN) | ||
629 | offset = S5PCSIS_PKTDATA_EVEN; | ||
630 | else | ||
631 | offset = S5PCSIS_PKTDATA_ODD; | ||
632 | |||
633 | memcpy(pktbuf->data, state->regs + offset, pktbuf->len); | ||
634 | pktbuf->data = NULL; | ||
635 | rmb(); | ||
636 | } | ||
637 | |||
588 | /* Update the event/error counters */ | 638 | /* Update the event/error counters */ |
589 | if ((status & S5PCSIS_INTSRC_ERRORS) || debug) { | 639 | if ((status & S5PCSIS_INTSRC_ERRORS) || debug) { |
590 | int i; | 640 | int i; |
@@ -620,14 +670,15 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) | |||
620 | spin_lock_init(&state->slock); | 670 | spin_lock_init(&state->slock); |
621 | 671 | ||
622 | state->pdev = pdev; | 672 | state->pdev = pdev; |
673 | state->index = max(0, pdev->id); | ||
623 | 674 | ||
624 | pdata = pdev->dev.platform_data; | 675 | pdata = pdev->dev.platform_data; |
625 | if (pdata == NULL || pdata->phy_enable == NULL) { | 676 | if (pdata == NULL) { |
626 | dev_err(&pdev->dev, "Platform data not fully specified\n"); | 677 | dev_err(&pdev->dev, "Platform data not fully specified\n"); |
627 | return -EINVAL; | 678 | return -EINVAL; |
628 | } | 679 | } |
629 | 680 | ||
630 | if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) || | 681 | if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) || |
631 | pdata->lanes > CSIS0_MAX_LANES) { | 682 | pdata->lanes > CSIS0_MAX_LANES) { |
632 | dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", | 683 | dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", |
633 | pdata->lanes); | 684 | pdata->lanes); |
@@ -710,7 +761,6 @@ e_clkput: | |||
710 | 761 | ||
711 | static int s5pcsis_pm_suspend(struct device *dev, bool runtime) | 762 | static int s5pcsis_pm_suspend(struct device *dev, bool runtime) |
712 | { | 763 | { |
713 | struct s5p_platform_mipi_csis *pdata = dev->platform_data; | ||
714 | struct platform_device *pdev = to_platform_device(dev); | 764 | struct platform_device *pdev = to_platform_device(dev); |
715 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); | 765 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); |
716 | struct csis_state *state = sd_to_csis_state(sd); | 766 | struct csis_state *state = sd_to_csis_state(sd); |
@@ -722,7 +772,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime) | |||
722 | mutex_lock(&state->lock); | 772 | mutex_lock(&state->lock); |
723 | if (state->flags & ST_POWERED) { | 773 | if (state->flags & ST_POWERED) { |
724 | s5pcsis_stop_stream(state); | 774 | s5pcsis_stop_stream(state); |
725 | ret = pdata->phy_enable(state->pdev, false); | 775 | ret = s5p_csis_phy_enable(state->index, false); |
726 | if (ret) | 776 | if (ret) |
727 | goto unlock; | 777 | goto unlock; |
728 | ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES, | 778 | ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES, |
@@ -741,7 +791,6 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime) | |||
741 | 791 | ||
742 | static int s5pcsis_pm_resume(struct device *dev, bool runtime) | 792 | static int s5pcsis_pm_resume(struct device *dev, bool runtime) |
743 | { | 793 | { |
744 | struct s5p_platform_mipi_csis *pdata = dev->platform_data; | ||
745 | struct platform_device *pdev = to_platform_device(dev); | 794 | struct platform_device *pdev = to_platform_device(dev); |
746 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); | 795 | struct v4l2_subdev *sd = platform_get_drvdata(pdev); |
747 | struct csis_state *state = sd_to_csis_state(sd); | 796 | struct csis_state *state = sd_to_csis_state(sd); |
@@ -759,7 +808,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime) | |||
759 | state->supplies); | 808 | state->supplies); |
760 | if (ret) | 809 | if (ret) |
761 | goto unlock; | 810 | goto unlock; |
762 | ret = pdata->phy_enable(state->pdev, true); | 811 | ret = s5p_csis_phy_enable(state->index, true); |
763 | if (!ret) { | 812 | if (!ret) { |
764 | state->flags |= ST_POWERED; | 813 | state->flags |= ST_POWERED; |
765 | } else { | 814 | } else { |