@@ -68,25 +68,27 @@ enum pixel_fmt {
IPU_PIX_FMT_GENERIC,
IPU_PIX_FMT_RGB332,
IPU_PIX_FMT_YUV420P,
+ IPU_PIX_FMT_YVU420P,
IPU_PIX_FMT_YUV422P,
- IPU_PIX_FMT_YUV420P2,
IPU_PIX_FMT_YVU422P,
/* 2 bytes */
+ IPU_PIX_FMT_GENERIC_16,
+ IPU_PIX_FMT_RGB444,
+ IPU_PIX_FMT_RGB555,
IPU_PIX_FMT_RGB565,
- IPU_PIX_FMT_RGB666,
- IPU_PIX_FMT_BGR666,
- IPU_PIX_FMT_YUYV,
IPU_PIX_FMT_UYVY,
/* 3 bytes */
- IPU_PIX_FMT_RGB24,
+ IPU_PIX_FMT_BGR666,
+ IPU_PIX_FMT_RGB666,
IPU_PIX_FMT_BGR24,
+ IPU_PIX_FMT_RGB24,
/* 4 bytes */
IPU_PIX_FMT_GENERIC_32,
- IPU_PIX_FMT_RGB32,
IPU_PIX_FMT_BGR32,
- IPU_PIX_FMT_ABGR32,
+ IPU_PIX_FMT_RGB32,
IPU_PIX_FMT_BGRA32,
IPU_PIX_FMT_RGBA32,
+ IPU_PIX_FMT_ABGR32,
};
enum ipu_color_space {
@@ -95,11 +95,15 @@ static uint32_t bytes_per_pixel(enum pixel_fmt fmt)
case IPU_PIX_FMT_GENERIC: /* generic data */
case IPU_PIX_FMT_RGB332:
case IPU_PIX_FMT_YUV420P:
+ case IPU_PIX_FMT_YVU420P:
case IPU_PIX_FMT_YUV422P:
+ case IPU_PIX_FMT_YVU422P:
default:
return 1;
+ case IPU_PIX_FMT_GENERIC_16: /* generic data */
+ case IPU_PIX_FMT_RGB444:
+ case IPU_PIX_FMT_RGB555:
case IPU_PIX_FMT_RGB565:
- case IPU_PIX_FMT_YUYV:
case IPU_PIX_FMT_UYVY:
return 2;
case IPU_PIX_FMT_BGR24:
@@ -108,6 +112,8 @@ static uint32_t bytes_per_pixel(enum pixel_fmt fmt)
case IPU_PIX_FMT_GENERIC_32: /* generic data */
case IPU_PIX_FMT_BGR32:
case IPU_PIX_FMT_RGB32:
+ case IPU_PIX_FMT_BGRA32:
+ case IPU_PIX_FMT_RGBA32:
case IPU_PIX_FMT_ABGR32:
return 4;
}
@@ -297,18 +303,64 @@ static void ipu_ch_param_set_size(union chan_param_mem *params,
switch (pixel_fmt) {
case IPU_PIX_FMT_GENERIC:
- /*Represents 8-bit Generic data */
- params->pp.bpp = 3;
- params->pp.pfs = 7;
- params->pp.npb = 31;
- params->pp.sat = 2; /* SAT = use 32-bit access */
+ /* Represents 8-bit Generic data */
+ params->ip.bpp = 3;
+ params->ip.pfs = 7;
+ params->ip.npb = 31;
+ params->ip.sat = 2; /* SAT = use 32-bit access */
+ break;
+ case IPU_PIX_FMT_GENERIC_16:
+ /* Represents 16-bit Generic data */
+ params->ip.bpp = 2;
+ params->ip.pfs = 7;
+ params->ip.npb = 15;
+ params->ip.sat = 2; /* SAT = use 32-bit access */
break;
case IPU_PIX_FMT_GENERIC_32:
- /*Represents 32-bit Generic data */
- params->pp.bpp = 0;
- params->pp.pfs = 7;
- params->pp.npb = 7;
- params->pp.sat = 2; /* SAT = use 32-bit access */
+ /* Represents 32-bit Generic data */
+ params->ip.bpp = 0;
+ params->ip.pfs = 7;
+ params->ip.npb = 7;
+ params->ip.sat = 2; /* SAT = use 32-bit access */
+ break;
+ case IPU_PIX_FMT_RGB332:
+ params->ip.bpp = 3;
+ params->ip.pfs = 4;
+ params->ip.npb = 31;
+ params->ip.sat = 2; /* SAT = 32-bit access */
+ params->ip.ofs0 = 0; /* Red bit offset */
+ params->ip.ofs1 = 3; /* Green bit offset */
+ params->ip.ofs2 = 6; /* Blue bit offset */
+ params->ip.ofs3 = 8; /* Alpha bit offset */
+ params->ip.wid0 = 2; /* Red bit width - 1 */
+ params->ip.wid1 = 2; /* Green bit width - 1 */
+ params->ip.wid2 = 1; /* Blue bit width - 1 */
+ break;
+ case IPU_PIX_FMT_RGB444:
+ params->ip.bpp = 2;
+ params->ip.pfs = 4;
+ params->ip.npb = 15;
+ params->ip.sat = 2; /* SAT = 32-bit access */
+ params->ip.ofs0 = 4; /* Red bit offset */
+ params->ip.ofs1 = 8; /* Green bit offset */
+ params->ip.ofs2 = 12; /* Blue bit offset */
+ params->ip.ofs3 = 16; /* Alpha bit offset */
+ params->ip.wid0 = 3; /* Red bit width - 1 */
+ params->ip.wid1 = 3; /* Green bit width - 1 */
+ params->ip.wid2 = 3; /* Blue bit width - 1 */
+ break;
+ case IPU_PIX_FMT_RGB555:
+ params->ip.bpp = 2;
+ params->ip.pfs = 4;
+ params->ip.npb = 15;
+ params->ip.sat = 2; /* SAT = 32-bit access */
+ params->ip.ofs0 = 1; /* Red bit offset */
+ params->ip.ofs1 = 6; /* Green bit offset */
+ params->ip.ofs2 = 11; /* Blue bit offset */
+ params->ip.ofs3 = 16; /* Alpha bit offset */
+ params->ip.wid0 = 4; /* Red bit width - 1 */
+ params->ip.wid1 = 4; /* Green bit width - 1 */
+ params->ip.wid2 = 4; /* Blue bit width - 1 */
break;
case IPU_PIX_FMT_RGB565:
params->ip.bpp = 2;
@@ -326,7 +378,7 @@ static void ipu_ch_param_set_size(union chan_param_mem *params,
case IPU_PIX_FMT_BGR24:
params->ip.bpp = 1; /* 24 BPP & RGB PFS */
params->ip.pfs = 4;
- params->ip.npb = 7;
+ params->ip.npb = 9;
params->ip.sat = 2; /* SAT = 32-bit access */
params->ip.ofs0 = 0; /* Red bit offset */
params->ip.ofs1 = 8; /* Green bit offset */
@@ -339,7 +391,7 @@ static void ipu_ch_param_set_size(union chan_param_mem *params,
case IPU_PIX_FMT_RGB24:
params->ip.bpp = 1; /* 24 BPP & RGB PFS */
params->ip.pfs = 4;
- params->ip.npb = 7;
+ params->ip.npb = 9;
params->ip.sat = 2; /* SAT = 32-bit access */
params->ip.ofs0 = 16; /* Red bit offset */
params->ip.ofs1 = 8; /* Green bit offset */
@@ -383,37 +435,45 @@ static void ipu_ch_param_set_size(union chan_param_mem *params,
case IPU_PIX_FMT_UYVY:
params->ip.bpp = 2;
params->ip.pfs = 6;
- params->ip.npb = 7;
+ params->ip.npb = 15;
params->ip.sat = 2; /* SAT = 32-bit access */
break;
- case IPU_PIX_FMT_YUV420P2:
case IPU_PIX_FMT_YUV420P:
- params->ip.bpp = 3;
- params->ip.pfs = 3;
- params->ip.npb = 7;
- params->ip.sat = 2; /* SAT = 32-bit access */
+ params->pp.bpp = 3;
+ params->pp.pfs = 3;
+ params->pp.npb = 15;
+ params->pp.sat = 2; /* SAT = 32-bit access */
u_offset = stride * height;
v_offset = u_offset + u_offset / 4;
ipu_ch_param_set_plane_offset(params, u_offset, v_offset);
break;
- case IPU_PIX_FMT_YVU422P:
- params->ip.bpp = 3;
- params->ip.pfs = 2;
- params->ip.npb = 7;
- params->ip.sat = 2; /* SAT = 32-bit access */
+ case IPU_PIX_FMT_YVU420P:
+ params->pp.bpp = 3;
+ params->pp.pfs = 3;
+ params->pp.npb = 15;
+ params->pp.sat = 2; /* SAT = 32-bit access */
v_offset = stride * height;
- u_offset = v_offset + v_offset / 2;
+ u_offset = v_offset + v_offset / 4;
ipu_ch_param_set_plane_offset(params, u_offset, v_offset);
break;
case IPU_PIX_FMT_YUV422P:
- params->ip.bpp = 3;
- params->ip.pfs = 2;
- params->ip.npb = 7;
- params->ip.sat = 2; /* SAT = 32-bit access */
+ params->pp.bpp = 3;
+ params->pp.pfs = 2;
+ params->pp.npb = 15;
+ params->pp.sat = 2; /* SAT = 32-bit access */
u_offset = stride * height;
v_offset = u_offset + u_offset / 2;
ipu_ch_param_set_plane_offset(params, u_offset, v_offset);
break;
+ case IPU_PIX_FMT_YVU422P:
+ params->pp.bpp = 3;
+ params->pp.pfs = 2;
+ params->pp.npb = 15;
+ params->pp.sat = 2; /* SAT = 32-bit access */
+ v_offset = stride * height;
+ u_offset = v_offset + v_offset / 2;
+ ipu_ch_param_set_plane_offset(params, u_offset, v_offset);
+ break;
default:
dev_err(ipu_data.dev,
"mx3 ipu: unimplemented pixel format %d\n", pixel_fmt);
@@ -423,6 +483,17 @@ static void ipu_ch_param_set_size(union chan_param_mem *params,
params->pp.nsb = 1;
}
+static uint16_t ipu_ch_param_get_burst_size(const union chan_param_mem *params)
+{
+ return params->pp.npb + 1;
+}
+
+static void ipu_ch_param_set_burst_size(union chan_param_mem *params,
+ uint16_t burst_pixels)
+{
+ params->pp.npb = burst_pixels - 1;
+}
+
static void ipu_ch_param_set_buffer(union chan_param_mem *params,
dma_addr_t buf0, dma_addr_t buf1)
{
@@ -498,6 +569,9 @@ static int calc_resize_coeffs(uint32_t in_size, uint32_t out_size,
static enum ipu_color_space format_to_colorspace(enum pixel_fmt fmt)
{
switch (fmt) {
+ case IPU_PIX_FMT_RGB332:
+ case IPU_PIX_FMT_RGB444:
+ case IPU_PIX_FMT_RGB555:
case IPU_PIX_FMT_RGB565:
case IPU_PIX_FMT_BGR24:
case IPU_PIX_FMT_RGB24:
@@ -665,6 +739,7 @@ static int ipu_init_channel_buffer(struct idmac_channel *ichan,
unsigned long flags;
uint32_t reg;
uint32_t stride_bytes;
+ uint16_t burst_pixels;
stride_bytes = stride * bytes_per_pixel(pixel_fmt);
@@ -685,6 +760,29 @@ static int ipu_init_channel_buffer(struct idmac_channel *ichan,
ipu_ch_param_set_size(¶ms, pixel_fmt, width, height, stride_bytes);
ipu_ch_param_set_buffer(¶ms, phyaddr_0, phyaddr_1);
ipu_ch_param_set_rotation(¶ms, rot_mode);
+ /*
+ * ipu_ch_param_set_size() above has set the optimal burst size for each
+ * format, which also helps avoiding hanging channels:
+ * - 16 pixels for planar formats,
+ * - 8, 10, 16, 32 or 64 pixels for interleaved formats.
+ */
+ burst_pixels = ipu_ch_param_get_burst_size(¶ms);
+ /* Some channels (rotation) have restriction on burst length */
+ switch (channel) {
+ case IDMAC_IC_0:
+ case IDMAC_IC_7:
+ default:
+ /* There is no restriction for these channels. */
+ break;
+ case IDMAC_SDC_0:
+ case IDMAC_SDC_1:
+ if (burst_pixels >= 16)
+ burst_pixels = 16;
+ else
+ burst_pixels = 8;
+ break;
+ }
+ ipu_ch_param_set_burst_size(¶ms, burst_pixels);
spin_lock_irqsave(&ipu->lock, flags);
@@ -1081,7 +1179,18 @@ static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan,
if (wait_for_stop && channel != IDMAC_SDC_1 && channel != IDMAC_SDC_0) {
timeout = 40;
- /* This waiting always fails. Related to spurious irq problem */
+ /*
+ * This waiting sometimes fails because the channel has hung.
+ * This can happen if the input stream is stopped for an active
+ * IDMAC channel.
+ * E.g., soc_camera_streamoff() induces a call to
+ * mx3_videobuf_release(), which does not (and does not have to)
+ * wait for the completion of DMA transfers before releasing the
+ * corresponding buffers, then soc_camera_streamoff() invokes
+ * sd->video->s_stream(), which may hence stop the input stream
+ * for an active IDMAC channel. The current code is called only
+ * afterwards.
+ */
while ((idmac_read_icreg(ipu, IDMAC_CHA_BUSY) & chan_mask) ||
(ipu_channel_status(ipu, channel) == TASK_STAT_ACTIVE)) {
timeout--;
@@ -1164,7 +1273,7 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
dma_async_tx_callback callback;
void *callback_param;
bool done = false;
- u32 ready0, ready1, curbuf, err;
+ u32 ready0, ready1, curbuf, err, busy;
unsigned long flags;
/* IDMAC has cleared the respective BUFx_RDY bit, we manage the buffer */
@@ -1177,7 +1286,15 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
ready1 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY);
curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF);
err = idmac_read_ipureg(&ipu_data, IPU_INT_STAT_4);
+ busy = idmac_read_icreg(&ipu_data, IDMAC_CHA_BUSY);
+ /*
+ * If the buffer transfer is too slow compared to the frame rate, an
+ * NF will occur before the expected channel EOF, triggering an
+ * NFB4EOF_ERR.
+ * This error interrupt has not been requested, so the code is handling
+ * the first channel EOF interrupt following this error.
+ */
if (err & (1 << chan_id)) {
idmac_write_ipureg(&ipu_data, 1 << chan_id, IPU_INT_STAT_4);
spin_unlock_irqrestore(&ipu_data.lock, flags);
@@ -1187,6 +1304,19 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
* you can force channel re-enable on the next tx_submit(), but
* this is dirty - think about descriptors with multiple
* sg elements.
+ * Passing back the incomplete buffer to the submitter would
+ * also be dirty.
+ * Reusing the incomplete buffer after the next one would imply
+ * changing the callback call order, which may affect the
+ * submitter.
+ * The cleanest thing to do here would be to tell the submitter
+ * that its buffer has been dropped and to handle the completion
+ * of the next buffer (i.e. the current interrupt).
+ * Unfortunately, there does not seem to be any means of doing
+ * that.
+ * If this error is not properly handled, the channel will hang
+ * since this will let the channel consume its two buffers
+ * without submitting new buffers.
*/
dev_warn(dev, "NFB4EOF on channel %d, ready %x, %x, cur %x\n",
chan_id, ready0, ready1, curbuf);
@@ -1196,6 +1326,51 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
/* Other interrupts do not interfere with this channel */
spin_lock(&ichan->lock);
+ /*
+ * The busy status has to be checked here in order to avoid considering
+ * a legitimate interrupt as a spurious interrupt.
+ * Actually, when double-buffering is used, the normal sequence for
+ * DMAIC_7 is:
+ * - Initialization of the channel with DBMS = 1 and two buffers:
+ * BUF0_RDY = 1, BUF1_RDY = 1, CUR_BUF = 1, BUSY = 0
+ * - First CSI_NF: The channel starts handling buffer 0:
+ * BUF0_RDY = 0, BUF1_RDY = 1, CUR_BUF = 0, BUSY = 1
+ * - CSI_EOF: The channel stops handling buffer 0:
+ * BUF0_RDY = 0, BUF1_RDY = 1, CUR_BUF = 0, BUSY = 0
+ * - EOF: The channel signals buffer 0 completion:
+ * BUF0_RDY = 0, BUF1_RDY = 1, CUR_BUF = 0, BUSY = 0
+ * - The interrupt handler prepares buffer 0 with the next element:
+ * BUF0_RDY = 1, BUF1_RDY = 1, CUR_BUF = 0, BUSY = 0
+ * - CSI_NF: The channel starts handling buffer 1:
+ * BUF0_RDY = 1, BUF1_RDY = 0, CUR_BUF = 1, BUSY = 1
+ * - CSI_EOF: The channel stops handling buffer 1:
+ * BUF0_RDY = 1, BUF1_RDY = 0, CUR_BUF = 1, BUSY = 0
+ * - EOF: The channel signals buffer 1 completion:
+ * BUF0_RDY = 1, BUF1_RDY = 0, CUR_BUF = 1, BUSY = 0
+ * - The interrupt handler prepares buffer 1 with the next element:
+ * BUF0_RDY = 1, BUF1_RDY = 1, CUR_BUF = 1, BUSY = 0
+ * - CSI_NF: The channel starts handling buffer 0:
+ * BUF0_RDY = 0, BUF1_RDY = 1, CUR_BUF = 0, BUSY = 1
+ * - ...
+ * Hence, a DMAIC_7_EOF interrupt can be received with CUR_BUF being the
+ * active buffer if there is enough time between two consecutive CSI
+ * frames. This is normal if the channel is not busy. This interrupt
+ * must be used to prepare the channel for a new buffer.
+ * If the channel is busy however, this is either a shared interrupt
+ * that is not for this channel, or a legitimate interrupt that has been
+ * delayed beyond the allowed update time window.
+ */
+ if (unlikely(chan_id != IDMAC_SDC_0 && chan_id != IDMAC_SDC_1 &&
+ (busy >> chan_id) & 1 &&
+ ((curbuf >> chan_id) & 1) == ichan->active_buffer)) {
+ spin_unlock(&ichan->lock);
+ dev_dbg(dev,
+ "IRQ on active buffer on channel %x, active "
+ "%d, ready %x, %x, current %x!\n", chan_id,
+ ichan->active_buffer, ready0, ready1, curbuf);
+ return IRQ_NONE;
+ }
+
if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) ||
(!ichan->active_buffer && (ready0 >> chan_id) & 1)
)) {
@@ -1219,8 +1394,8 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id)
/*
* active_buffer is a software flag, it shows which buffer we are
- * currently expecting back from the hardware, IDMAC should be
- * processing the other buffer already
+ * currently expecting back from the hardware, IDMAC should have
+ * finished processing this buffer already
*/
sg = &ichan->sg[ichan->active_buffer];
sgnext = ichan->sg[!ichan->active_buffer];
@@ -243,17 +243,66 @@ static int mx3_videobuf_setup(struct vb2_queue *vq,
return 0;
}
-static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
+static u32 pixelcode_to_csi_fmt(enum v4l2_mbus_pixelcode code)
{
- /* Add more formats as need arises and test possibilities appear... */
- switch (fourcc) {
- case V4L2_PIX_FMT_RGB24:
- return IPU_PIX_FMT_RGB24;
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_RGB565:
+ switch (code) {
+ /* CSI input formats (i.e. sensor output formats) */
+ case V4L2_MBUS_FMT_RGB24_3X8_BE:
+ return CSI_SENS_CONF_DATA_FMT_RGB_YUV444;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ return CSI_SENS_CONF_DATA_FMT_YUV422;
+ default:
+ return CSI_SENS_CONF_DATA_FMT_BAYER;
+ }
+}
+
+static enum pixel_fmt to_ipu_pix(enum v4l2_mbus_pixelcode code, __u32 fourcc)
+{
+ const struct soc_mbus_pixelfmt *fmt = soc_mbus_get_fmtdesc(code);
+
+ if (!fmt)
+ return -EINVAL;
+
+ switch (code) {
+ /* CSI input formats (i.e. sensor output formats) */
+ case V4L2_MBUS_FMT_RGB24_3X8_BE:
+ switch (fourcc) {
+ /* IDMAC output formats */
+ case V4L2_PIX_FMT_RGB332:
+ return IPU_PIX_FMT_RGB332;
+ case V4L2_PIX_FMT_RGB444:
+ return IPU_PIX_FMT_RGB444;
+ case V4L2_PIX_FMT_RGB555:
+ return IPU_PIX_FMT_RGB555;
+ case V4L2_PIX_FMT_RGB565:
+ return IPU_PIX_FMT_RGB565;
+ case V4L2_PIX_FMT_BGR24:
+ return IPU_PIX_FMT_BGR24;
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_PIX_FMT_RGB24;
+ }
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ switch (fourcc) {
+ /* IDMAC output formats */
+ case V4L2_PIX_FMT_YVU420:
+ return IPU_PIX_FMT_YVU420P;
+ case V4L2_PIX_FMT_UYVY:
+ return IPU_PIX_FMT_UYVY;
+ case V4L2_PIX_FMT_YUV422P:
+ return IPU_PIX_FMT_YUV422P;
+ case V4L2_PIX_FMT_YUV420:
+ return IPU_PIX_FMT_YUV420P;
+ }
+ break;
default:
- return IPU_PIX_FMT_GENERIC;
+ if (fmt->bits_per_sample <= 8)
+ return IPU_PIX_FMT_GENERIC;
+ else if (fmt->bits_per_sample <= 16)
+ return IPU_PIX_FMT_GENERIC_16;
+ break;
}
+ return -EINVAL;
}
static void mx3_videobuf_queue(struct vb2_buffer *vb)
@@ -268,6 +317,7 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
struct idmac_video_param *video = &ichan->params.video;
const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt);
+ s32 width = icd->user_width;
unsigned long flags;
dma_cookie_t cookie;
size_t new_size;
@@ -304,29 +354,30 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb)
vb2_set_plane_payload(vb, 0, new_size);
/* This is the configuration of one sg-element */
- video->out_pixel_fmt = fourcc_to_ipu_pix(host_fmt->fourcc);
+ video->out_pixel_fmt = to_ipu_pix(icd->current_fmt->code,
+ host_fmt->fourcc);
+ BUG_ON(video->out_pixel_fmt < 0);
- if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC) {
+ if (video->out_pixel_fmt == IPU_PIX_FMT_GENERIC ||
+ video->out_pixel_fmt == IPU_PIX_FMT_GENERIC_16) {
/*
* If the IPU DMA channel is configured to transfer generic
- * 8-bit data, we have to set up the geometry parameters
- * correctly, according to the current pixel format. The DMA
- * horizontal parameters in this case are expressed in bytes,
- * not in pixels.
+ * data, we have to set up the geometry parameters correctly,
+ * according to the current pixel format. The DMA horizontal
+ * parameters in this case are expressed in samples, not in
+ * pixels.
*/
- video->out_width = bytes_per_line;
- video->out_height = icd->user_height;
- video->out_stride = bytes_per_line;
- } else {
- /*
- * For IPU known formats the pixel unit will be managed
- * successfully by the IPU code
- */
- video->out_width = icd->user_width;
- video->out_height = icd->user_height;
- video->out_stride = icd->user_width;
+ unsigned int num, den;
+ int ret = soc_mbus_samples_per_pixel(icd->current_fmt->host_fmt,
+ &num, &den);
+ BUG_ON(ret < 0);
+ width = width * num / den;
}
+ video->out_width = width;
+ video->out_height = icd->user_height;
+ video->out_stride = width;
+
#ifdef DEBUG
/* helps to see what DMA actually has written */
if (vb2_plane_vaddr(vb, 0))
@@ -452,7 +503,7 @@ static struct vb2_ops mx3_videobuf_ops = {
};
static int mx3_camera_init_videobuf(struct vb2_queue *q,
- struct soc_camera_device *icd)
+ struct soc_camera_device *icd)
{
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR;
@@ -562,7 +613,7 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
{
/*
* If requested data width is supported by the platform, use it or any
- * possible lower value - i.MX31 is smart enough to shift bits
+ * possible lower value - i.MX3x is smart enough to shift bits
*/
if (buswidth > fls(mx3_cam->width_flags))
return -EINVAL;
@@ -587,16 +638,16 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam,
}
static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
- const unsigned int depth)
+ unsigned int buswidth)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx3_camera_dev *mx3_cam = ici->priv;
struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
unsigned long bus_flags, common_flags;
- int ret = test_platform_param(mx3_cam, depth, &bus_flags);
+ int ret = test_platform_param(mx3_cam, buswidth, &bus_flags);
- dev_dbg(icd->parent, "request bus width %d bit: %d\n", depth, ret);
+ dev_dbg(icd->parent, "requested bus width %d bit: %d\n", buswidth, ret);
if (ret < 0)
return ret;
@@ -635,30 +686,77 @@ static bool chan_filter(struct dma_chan *chan, void *arg)
pdata->dma_dev == chan->device->dev;
}
-static const struct soc_mbus_pixelfmt mx3_camera_formats[] = {
+static const struct soc_mbus_pixelfmt mx3_camera_formats_rgb[] = {
{
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .name = "Bayer BGGR (sRGB) 8 bit",
+ .fourcc = V4L2_PIX_FMT_RGB332,
+ .name = "Packed RGB332",
.bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .name = "Packed RGB444",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB555,
+ .name = "Packed RGB555",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "Packed RGB565",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .name = "BGR24",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_3X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .name = "RGB24",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_3X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+};
+
+static const struct soc_mbus_pixelfmt mx3_camera_formats_yuv[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .name = "Planar YVU 4:2:0 12 bit",
+ .bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
}, {
- .fourcc = V4L2_PIX_FMT_GREY,
- .name = "Monochrome 8 bit",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "Interleaved YUV 4:2:2 (UYVY) 16 bit",
.bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .name = "Planar YUV 4:2:2 16 bit",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .name = "Planar YUV 4:2:0 12 bit",
+ .bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
};
-/* This will be corrected as we get more formats */
-static bool mx3_camera_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+static bool mx3_camera_passthrough_supported(const struct soc_mbus_pixelfmt *fmt)
{
- return fmt->packing == SOC_MBUS_PACKING_NONE ||
- (fmt->bits_per_sample == 8 &&
- fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
- (fmt->bits_per_sample > 8 &&
- fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+ return fmt->bits_per_sample == 8 && fmt->order == SOC_MBUS_ORDER_LE;
}
static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int idx,
@@ -666,9 +764,9 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->parent;
- int formats = 0, ret;
+ int formats = 0, k, n = 0, ret;
enum v4l2_mbus_pixelcode code;
- const struct soc_mbus_pixelfmt *fmt;
+ const struct soc_mbus_pixelfmt *fmt, *mx3_camera_formats;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
if (ret < 0)
@@ -688,41 +786,42 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
return 0;
switch (code) {
- case V4L2_MBUS_FMT_SBGGR10_1X10:
- formats++;
- if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[0];
- xlate->code = code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
- mx3_camera_formats[0].name, code);
- }
+ /* CSI input formats (i.e. sensor output formats) */
+ case V4L2_MBUS_FMT_RGB24_3X8_BE:
+ mx3_camera_formats = mx3_camera_formats_rgb;
+ n = ARRAY_SIZE(mx3_camera_formats_rgb);
+ break;
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ mx3_camera_formats = mx3_camera_formats_yuv;
+ n = ARRAY_SIZE(mx3_camera_formats_yuv);
break;
case V4L2_MBUS_FMT_Y10_1X10:
- formats++;
- if (xlate) {
- xlate->host_fmt = &mx3_camera_formats[1];
- xlate->code = code;
- xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
- mx3_camera_formats[1].name, code);
- }
+ mx3_camera_formats = soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_Y16_1X16);
+ n = 1;
+ break;
+ case V4L2_MBUS_FMT_SBGGR10_1X10:
+ mx3_camera_formats = soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_SBGGR16_1X16);
+ n = 1;
break;
default:
- if (!mx3_camera_packing_supported(fmt))
- return 0;
+ if (mx3_camera_passthrough_supported(fmt)) {
+ mx3_camera_formats = fmt;
+ n = 1;
+ }
+ break;
}
- /* Generic pass-through */
- formats++;
- if (xlate) {
- xlate->host_fmt = fmt;
+ formats += n;
+ for (k = 0; xlate && k < n; k++) {
+ BUG_ON(!mx3_camera_formats);
+ xlate->host_fmt = &mx3_camera_formats[k];
xlate->code = code;
- dev_dbg(dev, "Providing format %c%c%c%c in pass-through mode\n",
- (fmt->fourcc >> (0*8)) & 0xFF,
- (fmt->fourcc >> (1*8)) & 0xFF,
- (fmt->fourcc >> (2*8)) & 0xFF,
- (fmt->fourcc >> (3*8)) & 0xFF);
+ dev_dbg(dev, "Providing format %c%c%c%c from media bus data format %d\n",
+ (xlate->host_fmt->fourcc >> (0*8)) & 0xFF,
+ (xlate->host_fmt->fourcc >> (1*8)) & 0xFF,
+ (xlate->host_fmt->fourcc >> (2*8)) & 0xFF,
+ (xlate->host_fmt->fourcc >> (3*8)) & 0xFF,
+ code);
xlate++;
}
@@ -731,11 +830,15 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
static void configure_geometry(struct mx3_camera_dev *mx3_cam,
unsigned int width, unsigned int height,
- const struct soc_mbus_pixelfmt *fmt)
+ enum v4l2_mbus_pixelcode code)
{
u32 ctrl, width_field, height_field;
+ const struct soc_mbus_pixelfmt *fmt;
- if (fourcc_to_ipu_pix(fmt->fourcc) == IPU_PIX_FMT_GENERIC) {
+ fmt = soc_mbus_get_fmtdesc(code);
+ BUG_ON(!fmt);
+
+ if (pixelcode_to_csi_fmt(code) == CSI_SENS_CONF_DATA_FMT_BAYER) {
/*
* As the CSI will be configured to output BAYER, here
* the width parameter count the number of samples to
@@ -835,8 +938,7 @@ static int mx3_camera_set_crop(struct soc_camera_device *icd,
}
if (mf.width != icd->user_width || mf.height != icd->user_height)
- configure_geometry(mx3_cam, mf.width, mf.height,
- icd->current_fmt->host_fmt);
+ configure_geometry(mx3_cam, mf.width, mf.height, mf.code);
dev_dbg(icd->parent, "Sensor cropped %dx%d\n",
mf.width, mf.height);
@@ -874,7 +976,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd,
* mxc_v4l2_s_fmt()
*/
- configure_geometry(mx3_cam, pix->width, pix->height, xlate->host_fmt);
+ configure_geometry(mx3_cam, pix->width, pix->height, xlate->code);
mf.width = pix->width;
mf.height = pix->height;
@@ -1089,10 +1191,8 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
(3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
(3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
- /* TODO: Support RGB and YUV formats */
-
- /* This has been set in mx3_camera_activate(), but we clear it above */
- sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
+ /* Set data format */
+ sens_conf |= pixelcode_to_csi_fmt(icd->current_fmt->code);
if (common_flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
@@ -1104,7 +1204,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd)
sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
/* Just do what we're asked to do */
- switch (xlate->host_fmt->bits_per_sample) {
+ switch (buswidth) {
case 4:
dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
break;
@@ -17,37 +17,37 @@
static const struct soc_mbus_lookup mbus_fmt[] = {
{
- .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .code = V4L2_MBUS_FMT_RGB332_1X8,
.fmt = {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .name = "YUYV",
+ .fourcc = V4L2_PIX_FMT_RGB332,
+ .name = "RGB332",
.bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .packing = SOC_MBUS_PACKING_1X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_YVYU8_2X8,
+ .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE,
.fmt = {
- .fourcc = V4L2_PIX_FMT_YVYU,
- .name = "YVYU",
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .name = "RGB444",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_LE,
+ .order = SOC_MBUS_ORDER_BE,
},
}, {
- .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
.fmt = {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .name = "UYVY",
+ .fourcc = V4L2_PIX_FMT_RGB444,
+ .name = "RGB444",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_VYUY8_2X8,
+ .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
.fmt = {
- .fourcc = V4L2_PIX_FMT_VYUY,
- .name = "VYUY",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "RGB555X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
@@ -62,10 +62,10 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE,
+ .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
.fmt = {
- .fourcc = V4L2_PIX_FMT_RGB555X,
- .name = "RGB555X",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+ .name = "RGB565X",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
@@ -80,30 +80,21 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_RGB565_2X8_BE,
+ .code = V4L2_MBUS_FMT_RGB24_3X8_BE,
.fmt = {
- .fourcc = V4L2_PIX_FMT_RGB565X,
- .name = "RGB565X",
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .name = "RGB24",
.bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .packing = SOC_MBUS_PACKING_3X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .code = V4L2_MBUS_FMT_RGB24_3X8_LE,
.fmt = {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .name = "Bayer 8 BGGR",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .name = "BGR24",
.bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_NONE,
- .order = SOC_MBUS_ORDER_LE,
- },
-}, {
- .code = V4L2_MBUS_FMT_SBGGR10_1X10,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .name = "Bayer 10 BGGR",
- .bits_per_sample = 10,
- .packing = SOC_MBUS_PACKING_EXTEND16,
+ .packing = SOC_MBUS_PACKING_3X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
},
}, {
@@ -116,84 +107,75 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_Y10_1X10,
+ .code = V4L2_MBUS_FMT_YUYV8_1_5X8,
.fmt = {
- .fourcc = V4L2_PIX_FMT_Y10,
- .name = "Grey 10bit",
- .bits_per_sample = 10,
- .packing = SOC_MBUS_PACKING_EXTEND16,
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .name = "YUYV 4:2:0",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
+ .code = V4L2_MBUS_FMT_YVYU8_1_5X8,
.fmt = {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .name = "Bayer 10 BGGR",
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .name = "YVYU 4:2:0",
.bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .packing = SOC_MBUS_PACKING_1_5X8,
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
.fmt = {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .name = "Bayer 10 BGGR",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
.bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADLO,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
+ .code = V4L2_MBUS_FMT_VYUY8_2X8,
.fmt = {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .name = "Bayer 10 BGGR",
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .name = "VYUY",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
+ .order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
.fmt = {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .name = "Bayer 10 BGGR",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .name = "YUYV",
.bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_2X8_PADLO,
- .order = SOC_MBUS_ORDER_BE,
- },
-}, {
- .code = V4L2_MBUS_FMT_JPEG_1X8,
- .fmt = {
- .fourcc = V4L2_PIX_FMT_JPEG,
- .name = "JPEG",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_VARIABLE,
- .order = SOC_MBUS_ORDER_LE,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE,
+ .code = V4L2_MBUS_FMT_YVYU8_2X8,
.fmt = {
- .fourcc = V4L2_PIX_FMT_RGB444,
- .name = "RGB444",
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .name = "YVYU",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_2X8_PADHI,
- .order = SOC_MBUS_ORDER_BE,
+ .order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_YUYV8_1_5X8,
+ .code = V4L2_MBUS_FMT_Y10_1X10,
.fmt = {
- .fourcc = V4L2_PIX_FMT_YUV420,
- .name = "YUYV 4:2:0",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_1_5X8,
+ .fourcc = V4L2_PIX_FMT_Y10,
+ .name = "Grey 10bit",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
},
}, {
- .code = V4L2_MBUS_FMT_YVYU8_1_5X8,
+ .code = V4L2_MBUS_FMT_Y12_1X12,
.fmt = {
- .fourcc = V4L2_PIX_FMT_YVU420,
- .name = "YVYU 4:2:0",
- .bits_per_sample = 8,
- .packing = SOC_MBUS_PACKING_1_5X8,
+ .fourcc = V4L2_PIX_FMT_Y12,
+ .name = "Grey 12bit",
+ .bits_per_sample = 12,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
},
}, {
@@ -233,6 +215,33 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.order = SOC_MBUS_ORDER_LE,
},
}, {
+ .code = V4L2_MBUS_FMT_Y16_1X16,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_Y16,
+ .name = "Grey 16bit",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .name = "Bayer 8 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SGBRG8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .name = "Bayer 8 GBRG",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
.code = V4L2_MBUS_FMT_SGRBG8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG8,
@@ -242,15 +251,69 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.order = SOC_MBUS_ORDER_LE,
},
}, {
+ .code = V4L2_MBUS_FMT_SRGGB8_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .name = "Bayer 8 RGGB",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
.code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGRBG10DPCM8,
- .name = "Bayer 10 BGGR DPCM 8",
+ .name = "Bayer 10 GRBG DPCM 8",
.bits_per_sample = 8,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADLO,
+ .order = SOC_MBUS_ORDER_BE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADLO,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR10_1X10,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .name = "Bayer 10 BGGR",
+ .bits_per_sample = 10,
+ .packing = SOC_MBUS_PACKING_EXTEND16,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
.code = V4L2_MBUS_FMT_SGBRG10_1X10,
.fmt = {
.fourcc = V4L2_PIX_FMT_SGBRG10,
@@ -313,6 +376,24 @@ static const struct soc_mbus_lookup mbus_fmt[] = {
.packing = SOC_MBUS_PACKING_EXTEND16,
.order = SOC_MBUS_ORDER_LE,
},
+}, {
+ .code = V4L2_MBUS_FMT_SBGGR16_1X16,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .name = "Bayer 16 BGGR",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
+}, {
+ .code = V4L2_MBUS_FMT_JPEG_1X8,
+ .fmt = {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .name = "JPEG",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_VARIABLE,
+ .order = SOC_MBUS_ORDER_LE,
+ },
},
};
@@ -321,18 +402,33 @@ int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf,
{
switch (mf->packing) {
case SOC_MBUS_PACKING_NONE:
+ case SOC_MBUS_PACKING_1X8_PADHI:
+ case SOC_MBUS_PACKING_1X8_PADLO:
+ case SOC_MBUS_PACKING_EXTEND8:
case SOC_MBUS_PACKING_EXTEND16:
+ case SOC_MBUS_PACKING_EXTEND24:
+ case SOC_MBUS_PACKING_EXTEND32:
*numerator = 1;
*denominator = 1;
return 0;
+ case SOC_MBUS_PACKING_1_5X8:
+ *numerator = 3;
+ *denominator = 2;
+ return 0;
case SOC_MBUS_PACKING_2X8_PADHI:
case SOC_MBUS_PACKING_2X8_PADLO:
*numerator = 2;
*denominator = 1;
return 0;
- case SOC_MBUS_PACKING_1_5X8:
+ case SOC_MBUS_PACKING_3X8_PADHI:
+ case SOC_MBUS_PACKING_3X8_PADLO:
*numerator = 3;
- *denominator = 2;
+ *denominator = 1;
+ return 0;
+ case SOC_MBUS_PACKING_4X8_PADHI:
+ case SOC_MBUS_PACKING_4X8_PADLO:
+ *numerator = 4;
+ *denominator = 1;
return 0;
case SOC_MBUS_PACKING_VARIABLE:
*numerator = 0;
@@ -348,12 +444,24 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
switch (mf->packing) {
case SOC_MBUS_PACKING_NONE:
return width * mf->bits_per_sample / 8;
+ case SOC_MBUS_PACKING_1X8_PADHI:
+ case SOC_MBUS_PACKING_1X8_PADLO:
+ case SOC_MBUS_PACKING_EXTEND8:
+ return width * 1;
+ case SOC_MBUS_PACKING_1_5X8:
+ return width * 3 / 2;
case SOC_MBUS_PACKING_2X8_PADHI:
case SOC_MBUS_PACKING_2X8_PADLO:
case SOC_MBUS_PACKING_EXTEND16:
return width * 2;
- case SOC_MBUS_PACKING_1_5X8:
- return width * 3 / 2;
+ case SOC_MBUS_PACKING_3X8_PADHI:
+ case SOC_MBUS_PACKING_3X8_PADLO:
+ case SOC_MBUS_PACKING_EXTEND24:
+ return width * 3;
+ case SOC_MBUS_PACKING_4X8_PADHI:
+ case SOC_MBUS_PACKING_4X8_PADLO:
+ case SOC_MBUS_PACKING_EXTEND32:
+ return width * 4;
case SOC_MBUS_PACKING_VARIABLE:
return 0;
}
@@ -37,7 +37,8 @@
enum v4l2_mbus_pixelcode {
V4L2_MBUS_FMT_FIXED = 0x0001,
- /* RGB - next is 0x1009 */
+ /* RGB - next is 0x100c */
+ V4L2_MBUS_FMT_RGB332_1X8 = 0x1009,
V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE = 0x1001,
V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE = 0x1002,
V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE = 0x1003,
@@ -46,8 +47,10 @@ enum v4l2_mbus_pixelcode {
V4L2_MBUS_FMT_BGR565_2X8_LE = 0x1006,
V4L2_MBUS_FMT_RGB565_2X8_BE = 0x1007,
V4L2_MBUS_FMT_RGB565_2X8_LE = 0x1008,
+ V4L2_MBUS_FMT_RGB24_3X8_BE = 0x100a,
+ V4L2_MBUS_FMT_RGB24_3X8_LE = 0x100b,
- /* YUV (including grey) - next is 0x2014 */
+ /* YUV (including grey) - next is 0x2015 */
V4L2_MBUS_FMT_Y8_1X8 = 0x2001,
V4L2_MBUS_FMT_UYVY8_1_5X8 = 0x2002,
V4L2_MBUS_FMT_VYUY8_1_5X8 = 0x2003,
@@ -65,10 +68,11 @@ enum v4l2_mbus_pixelcode {
V4L2_MBUS_FMT_VYUY8_1X16 = 0x2010,
V4L2_MBUS_FMT_YUYV8_1X16 = 0x2011,
V4L2_MBUS_FMT_YVYU8_1X16 = 0x2012,
+ V4L2_MBUS_FMT_Y16_1X16 = 0x2014,
V4L2_MBUS_FMT_YUYV10_1X20 = 0x200d,
V4L2_MBUS_FMT_YVYU10_1X20 = 0x200e,
- /* Bayer - next is 0x3015 */
+ /* Bayer - next is 0x3016 */
V4L2_MBUS_FMT_SBGGR8_1X8 = 0x3001,
V4L2_MBUS_FMT_SGBRG8_1X8 = 0x3013,
V4L2_MBUS_FMT_SGRBG8_1X8 = 0x3002,
@@ -89,6 +93,7 @@ enum v4l2_mbus_pixelcode {
V4L2_MBUS_FMT_SGBRG12_1X12 = 0x3010,
V4L2_MBUS_FMT_SGRBG12_1X12 = 0x3011,
V4L2_MBUS_FMT_SRGGB12_1X12 = 0x3012,
+ V4L2_MBUS_FMT_SBGGR16_1X16 = 0x3015,
/* JPEG compressed formats - next is 0x4002 */
V4L2_MBUS_FMT_JPEG_1X8 = 0x4001,
@@ -18,22 +18,46 @@
* enum soc_mbus_packing - data packing types on the media-bus
* @SOC_MBUS_PACKING_NONE: no packing, bit-for-bit transfer to RAM, one
* sample represents one pixel
+ * @SOC_MBUS_PACKING_1X8_PADHI: 8 bits transferred in 1 8-bit sample, in the
+ * possibly incomplete byte high bits are padding
+ * @SOC_MBUS_PACKING_1X8_PADLO: as above, but low bits are padding
+ * @SOC_MBUS_PACKING_EXTEND8: sample width (e.g., 4 bits) has to be extended
+ * to 8 bits
+ * @SOC_MBUS_PACKING_1_5X8: used for packed YUV 4:2:0 formats, where 4
+ * pixels occupy 6 bytes in RAM
* @SOC_MBUS_PACKING_2X8_PADHI: 16 bits transferred in 2 8-bit samples, in the
* possibly incomplete byte high bits are padding
* @SOC_MBUS_PACKING_2X8_PADLO: as above, but low bits are padding
* @SOC_MBUS_PACKING_EXTEND16: sample width (e.g., 10 bits) has to be extended
* to 16 bits
+ * @SOC_MBUS_PACKING_3X8_PADHI: 24 bits transferred in 3 8-bit samples, in the
+ * possibly incomplete byte high bits are padding
+ * @SOC_MBUS_PACKING_3X8_PADLO: as above, but low bits are padding
+ * @SOC_MBUS_PACKING_EXTEND24: sample width (e.g., 10 bits) has to be extended
+ * to 24 bits
+ * @SOC_MBUS_PACKING_4X8_PADHI: 32 bits transferred in 4 8-bit samples, in the
+ * possibly incomplete byte high bits are padding
+ * @SOC_MBUS_PACKING_4X8_PADLO: as above, but low bits are padding
+ * @SOC_MBUS_PACKING_EXTEND32: sample width (e.g., 10 bits) has to be extended
+ * to 32 bits
* @SOC_MBUS_PACKING_VARIABLE: compressed formats with variable packing
- * @SOC_MBUS_PACKING_1_5X8: used for packed YUV 4:2:0 formats, where 4
- * pixels occupy 6 bytes in RAM
*/
enum soc_mbus_packing {
SOC_MBUS_PACKING_NONE,
+ SOC_MBUS_PACKING_1X8_PADHI,
+ SOC_MBUS_PACKING_1X8_PADLO,
+ SOC_MBUS_PACKING_EXTEND8,
+ SOC_MBUS_PACKING_1_5X8,
SOC_MBUS_PACKING_2X8_PADHI,
SOC_MBUS_PACKING_2X8_PADLO,
SOC_MBUS_PACKING_EXTEND16,
+ SOC_MBUS_PACKING_3X8_PADHI,
+ SOC_MBUS_PACKING_3X8_PADLO,
+ SOC_MBUS_PACKING_EXTEND24,
+ SOC_MBUS_PACKING_4X8_PADHI,
+ SOC_MBUS_PACKING_4X8_PADLO,
+ SOC_MBUS_PACKING_EXTEND32,
SOC_MBUS_PACKING_VARIABLE,
- SOC_MBUS_PACKING_1_5X8,
};
/**
Hi all, This is an RFC for a patch completing full video capture support on i.MX3x. It adds missing video formats and automatic format associations according to the underlying sensor capabilities. It also fixes a spurious IPU interrupt issue that I have encountered on i.MX31 with earlier kernel versions. This might already have been fixed by some of the changes that occurred in the IPU driver since then, but I still have to test if my fix is still useful or not. Anyway, this should of course be split away to a separate patch. This patch has been successfully tested with i.MX35 and MT9M131, as well as some not yet mainline OmniVision sensor drivers, using all sensor-and-SoC-supported formats. This patch still has to be rebased against the latest kernel and refactored in the following way: 1. Media formats. 2. IPU formats. 3. IPU spurious interrupt fix (if still required). 4. mx3_camera formats. Comments are welcome, especially regarding possible conflicts with other IPU users. Best regards, Benoît Cc: Mauro Carvalho Chehab <mchehab@infradead.org> Cc: <linux-media@vger.kernel.org> Cc: Sascha Hauer <kernel@pengutronix.de> Cc: <linux-arm-kernel@lists.infradead.org> Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com> --- .../arch/arm/plat-mxc/include/mach/ipu.h | 16 +- .../drivers/dma/ipu/ipu_idmac.c | 241 ++++++++++++++--- .../drivers/media/video/mx3_camera.c | 264 +++++++++++++------ .../drivers/media/video/soc_mediabus.c | 276 ++++++++++++++------ .../include/linux/v4l2-mediabus.h | 11 +- .../include/media/soc_mediabus.h | 30 ++- 6 files changed, 626 insertions(+), 212 deletions(-)