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 e92236a..4c961b1 100644 --- a/drivers/media/platform/s5p-fimc/mipi-csis.c +++ b/drivers/media/platform/s5p-fimc/mipi-csis.c @@ -2,7 +2,7 @@ * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver * * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki, <s.nawrocki@samsung.com> + * Sylwester Nawrocki <s.nawrocki@samsung.com> * * This program is free software; you can redistribute it and/or modify * 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)"); #define CSIS_MAX_PIX_WIDTH 0xffff #define CSIS_MAX_PIX_HEIGHT 0xffff +/* Non-image packet data buffers */ +#define S5PCSIS_PKTDATA_ODD 0x2000 +#define S5PCSIS_PKTDATA_EVEN 0x3000 +#define S5PCSIS_PKTDATA_SIZE SZ_4K + enum { CSIS_CLK_MUX, CSIS_CLK_GATE, @@ -110,8 +115,8 @@ static char *csi_clock_name[] = { #define NUM_CSIS_CLOCKS ARRAY_SIZE(csi_clock_name) static const char * const csis_supply_name[] = { - "vdd11", /* 1.1V or 1.2V (s5pc100) MIPI CSI suppply */ - "vdd18", /* VDD 1.8V and MIPI CSI PLL supply */ + "vddcore", /* CSIS Core (1.0V, 1.1V or 1.2V) suppply */ + "vddio", /* CSIS I/O and PLL (1.8V) supply */ }; #define CSIS_NUM_SUPPLIES ARRAY_SIZE(csis_supply_name) @@ -144,12 +149,18 @@ static const struct s5pcsis_event s5pcsis_events[] = { }; #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events) +struct csis_pktbuf { + u32 *data; + unsigned int len; +}; + /** * struct csis_state - the driver's internal state data structure * @lock: mutex serializing the subdev and power management operations, * protecting @format and @flags members * @pads: CSIS pads array * @sd: v4l2_subdev associated with CSIS device instance + * @index: the hardware instance index * @pdev: CSIS platform device * @regs: mmaped I/O registers memory * @supplies: CSIS regulator supplies @@ -159,12 +170,14 @@ static const struct s5pcsis_event s5pcsis_events[] = { * @csis_fmt: current CSIS pixel format * @format: common media bus format for the source and sink pad * @slock: spinlock protecting structure members below + * @pkt_buf: the frame embedded (non-image) data buffer * @events: MIPI-CSIS event (error) counters */ struct csis_state { struct mutex lock; struct media_pad pads[CSIS_PADS_NUM]; struct v4l2_subdev sd; + u8 index; struct platform_device *pdev; void __iomem *regs; struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; @@ -175,6 +188,7 @@ struct csis_state { struct v4l2_mbus_framefmt format; struct spinlock slock; + struct csis_pktbuf pkt_buf; struct s5pcsis_event events[S5PCSIS_NUM_EVENTS]; }; @@ -202,7 +216,11 @@ static const struct csis_pix_format s5pcsis_formats[] = { .code = V4L2_MBUS_FMT_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), .data_alignment = 32, - }, + }, { + .code = V4L2_MBUS_FMT_S5C_UYVY_JPEG_1X8, + .fmt_reg = S5PCSIS_CFG_FMT_USER(1), + .data_alignment = 32, + } }; #define s5pcsis_write(__csis, __r, __v) writel(__v, __csis->regs + __r) @@ -266,7 +284,7 @@ static void __s5pcsis_set_format(struct csis_state *state) struct v4l2_mbus_framefmt *mf = &state->format; u32 val; - v4l2_dbg(1, debug, &state->sd, "fmt: %d, %d x %d\n", + v4l2_dbg(1, debug, &state->sd, "fmt: %#x, %d x %d\n", mf->code, mf->width, mf->height); /* Color format */ @@ -304,8 +322,10 @@ static void s5pcsis_set_params(struct csis_state *state) val |= S5PCSIS_CTRL_ALIGN_32BIT; else /* 24-bits */ val &= ~S5PCSIS_CTRL_ALIGN_32BIT; - /* Not using external clock. */ + val &= ~S5PCSIS_CTRL_WCLK_EXTCLK; + if (pdata->wclk_source) + val |= S5PCSIS_CTRL_WCLK_EXTCLK; s5pcsis_write(state, S5PCSIS_CTRL, val); /* Update the shadow register. */ @@ -529,6 +549,22 @@ static int s5pcsis_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } +static int s5pcsis_s_rx_buffer(struct v4l2_subdev *sd, void *buf, + unsigned int *size) +{ + struct csis_state *state = sd_to_csis_state(sd); + unsigned long flags; + + *size = min_t(unsigned int, *size, S5PCSIS_PKTDATA_SIZE); + + spin_lock_irqsave(&state->slock, flags); + state->pkt_buf.data = buf; + state->pkt_buf.len = *size; + spin_unlock_irqrestore(&state->slock, flags); + + return 0; +} + static int s5pcsis_log_status(struct v4l2_subdev *sd) { struct csis_state *state = sd_to_csis_state(sd); @@ -566,6 +602,7 @@ static struct v4l2_subdev_pad_ops s5pcsis_pad_ops = { }; static struct v4l2_subdev_video_ops s5pcsis_video_ops = { + .s_rx_buffer = s5pcsis_s_rx_buffer, .s_stream = s5pcsis_s_stream, }; @@ -578,13 +615,26 @@ static struct v4l2_subdev_ops s5pcsis_subdev_ops = { static irqreturn_t s5pcsis_irq_handler(int irq, void *dev_id) { struct csis_state *state = dev_id; + struct csis_pktbuf *pktbuf = &state->pkt_buf; unsigned long flags; u32 status; status = s5pcsis_read(state, S5PCSIS_INTSRC); - spin_lock_irqsave(&state->slock, flags); + if ((status & S5PCSIS_INTSRC_NON_IMAGE_DATA) && pktbuf->data) { + u32 offset; + + if (status & S5PCSIS_INTSRC_EVEN) + offset = S5PCSIS_PKTDATA_EVEN; + else + offset = S5PCSIS_PKTDATA_ODD; + + memcpy(pktbuf->data, state->regs + offset, pktbuf->len); + pktbuf->data = NULL; + rmb(); + } + /* Update the event/error counters */ if ((status & S5PCSIS_INTSRC_ERRORS) || debug) { int i; @@ -620,14 +670,15 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev) spin_lock_init(&state->slock); state->pdev = pdev; + state->index = max(0, pdev->id); pdata = pdev->dev.platform_data; - if (pdata == NULL || pdata->phy_enable == NULL) { + if (pdata == NULL) { dev_err(&pdev->dev, "Platform data not fully specified\n"); return -EINVAL; } - if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) || + if ((state->index == 1 && pdata->lanes > CSIS1_MAX_LANES) || pdata->lanes > CSIS0_MAX_LANES) { dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n", pdata->lanes); @@ -710,7 +761,6 @@ e_clkput: static int s5pcsis_pm_suspend(struct device *dev, bool runtime) { - struct s5p_platform_mipi_csis *pdata = dev->platform_data; struct platform_device *pdev = to_platform_device(dev); struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct csis_state *state = sd_to_csis_state(sd); @@ -722,7 +772,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime) mutex_lock(&state->lock); if (state->flags & ST_POWERED) { s5pcsis_stop_stream(state); - ret = pdata->phy_enable(state->pdev, false); + ret = s5p_csis_phy_enable(state->index, false); if (ret) goto unlock; ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES, @@ -741,7 +791,6 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime) static int s5pcsis_pm_resume(struct device *dev, bool runtime) { - struct s5p_platform_mipi_csis *pdata = dev->platform_data; struct platform_device *pdev = to_platform_device(dev); struct v4l2_subdev *sd = platform_get_drvdata(pdev); struct csis_state *state = sd_to_csis_state(sd); @@ -759,7 +808,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime) state->supplies); if (ret) goto unlock; - ret = pdata->phy_enable(state->pdev, true); + ret = s5p_csis_phy_enable(state->index, true); if (!ret) { state->flags |= ST_POWERED; } else { |