|
@@ -144,30 +144,6 @@ static struct sh_mobile_ceu_buffer *to_ceu_vb(struct vb2_buffer *vb)
|
|
|
return container_of(vb, struct sh_mobile_ceu_buffer, vb);
|
|
|
}
|
|
|
|
|
|
-static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- flags = SOCAM_MASTER |
|
|
|
- SOCAM_PCLK_SAMPLE_RISING |
|
|
|
- SOCAM_HSYNC_ACTIVE_HIGH |
|
|
|
- SOCAM_HSYNC_ACTIVE_LOW |
|
|
|
- SOCAM_VSYNC_ACTIVE_HIGH |
|
|
|
- SOCAM_VSYNC_ACTIVE_LOW |
|
|
|
- SOCAM_DATA_ACTIVE_HIGH;
|
|
|
-
|
|
|
- if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS)
|
|
|
- flags |= SOCAM_DATAWIDTH_8;
|
|
|
-
|
|
|
- if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS)
|
|
|
- flags |= SOCAM_DATAWIDTH_16;
|
|
|
-
|
|
|
- if (flags & SOCAM_DATAWIDTH_MASK)
|
|
|
- return flags;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
static void ceu_write(struct sh_mobile_ceu_dev *priv,
|
|
|
unsigned long reg_offs, u32 data)
|
|
|
{
|
|
@@ -737,66 +713,90 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr)
|
|
|
ceu_write(pcdev, CAPSR, capsr);
|
|
|
}
|
|
|
|
|
|
+/* Find the bus subdevice driver, e.g., CSI2 */
|
|
|
+static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev,
|
|
|
+ struct soc_camera_device *icd)
|
|
|
+{
|
|
|
+ if (!pcdev->csi2_pdev)
|
|
|
+ return soc_camera_to_subdev(icd);
|
|
|
+
|
|
|
+ return find_csi2(pcdev);
|
|
|
+}
|
|
|
+
|
|
|
+#define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \
|
|
|
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
|
|
|
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
|
|
|
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
|
|
|
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
|
|
|
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
|
|
|
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
|
|
|
+
|
|
|
/* Capture is not running, no interrupts, no locking needed */
|
|
|
static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
|
|
|
__u32 pixfmt)
|
|
|
{
|
|
|
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
|
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
|
|
- int ret;
|
|
|
- unsigned long camera_flags, common_flags, value;
|
|
|
- int yuv_lineskip;
|
|
|
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
|
|
|
struct sh_mobile_ceu_cam *cam = icd->host_priv;
|
|
|
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
|
|
+ unsigned long value, common_flags = CEU_BUS_FLAGS;
|
|
|
u32 capsr = capture_save_reset(pcdev);
|
|
|
+ unsigned int yuv_lineskip;
|
|
|
+ int ret;
|
|
|
|
|
|
- camera_flags = icd->ops->query_bus_param(icd);
|
|
|
- common_flags = soc_camera_bus_param_compatible(camera_flags,
|
|
|
- make_bus_param(pcdev));
|
|
|
- if (!common_flags)
|
|
|
- return -EINVAL;
|
|
|
+ /*
|
|
|
+ * If the client doesn't implement g_mbus_config, we just use our
|
|
|
+ * platform data
|
|
|
+ */
|
|
|
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
|
|
+ if (!ret) {
|
|
|
+ common_flags = soc_mbus_config_compatible(&cfg,
|
|
|
+ common_flags);
|
|
|
+ if (!common_flags)
|
|
|
+ return -EINVAL;
|
|
|
+ } else if (ret != -ENOIOCTLCMD) {
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
|
|
|
/* Make choises, based on platform preferences */
|
|
|
- if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
|
|
|
- (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
|
|
|
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
|
|
|
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
|
|
|
if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW)
|
|
|
- common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
|
|
|
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
|
|
|
else
|
|
|
- common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
|
|
|
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
|
|
|
}
|
|
|
|
|
|
- if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
|
|
|
- (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
|
|
|
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
|
|
|
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
|
|
|
if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW)
|
|
|
- common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
|
|
|
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
|
|
|
else
|
|
|
- common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
|
|
|
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
|
|
|
}
|
|
|
|
|
|
- ret = icd->ops->set_bus_param(icd, common_flags);
|
|
|
- if (ret < 0)
|
|
|
+ cfg.flags = common_flags;
|
|
|
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
|
|
|
+ if (ret < 0 && ret != -ENOIOCTLCMD)
|
|
|
return ret;
|
|
|
|
|
|
- switch (common_flags & SOCAM_DATAWIDTH_MASK) {
|
|
|
- case SOCAM_DATAWIDTH_8:
|
|
|
- pcdev->is_16bit = 0;
|
|
|
- break;
|
|
|
- case SOCAM_DATAWIDTH_16:
|
|
|
+ if (icd->current_fmt->host_fmt->bits_per_sample > 8)
|
|
|
pcdev->is_16bit = 1;
|
|
|
- break;
|
|
|
- default:
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
+ else
|
|
|
+ pcdev->is_16bit = 0;
|
|
|
|
|
|
ceu_write(pcdev, CRCNTR, 0);
|
|
|
ceu_write(pcdev, CRCMPR, 0);
|
|
|
|
|
|
value = 0x00000010; /* data fetch by default */
|
|
|
- yuv_lineskip = 0;
|
|
|
+ yuv_lineskip = 0x10;
|
|
|
|
|
|
switch (icd->current_fmt->host_fmt->fourcc) {
|
|
|
case V4L2_PIX_FMT_NV12:
|
|
|
case V4L2_PIX_FMT_NV21:
|
|
|
- yuv_lineskip = 1; /* skip for NV12/21, no skip for NV16/61 */
|
|
|
+ /* convert 4:2:2 -> 4:2:0 */
|
|
|
+ yuv_lineskip = 0; /* skip for NV12/21, no skip for NV16/61 */
|
|
|
/* fall-through */
|
|
|
case V4L2_PIX_FMT_NV16:
|
|
|
case V4L2_PIX_FMT_NV61:
|
|
@@ -822,8 +822,8 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
|
|
|
icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV61)
|
|
|
value ^= 0x00000100; /* swap U, V to change from NV1x->NVx1 */
|
|
|
|
|
|
- value |= common_flags & SOCAM_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
|
|
|
- value |= common_flags & SOCAM_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
|
|
|
+ value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0;
|
|
|
+ value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0;
|
|
|
value |= pcdev->is_16bit ? 1 << 12 : 0;
|
|
|
|
|
|
/* CSI2 mode */
|
|
@@ -866,9 +866,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
|
|
|
* using 7 we swap the data bytes to match the incoming order:
|
|
|
* D0, D1, D2, D3, D4, D5, D6, D7
|
|
|
*/
|
|
|
- value = 0x00000017;
|
|
|
- if (yuv_lineskip)
|
|
|
- value &= ~0x00000010; /* convert 4:2:2 -> 4:2:0 */
|
|
|
+ value = 0x00000007 | yuv_lineskip;
|
|
|
|
|
|
ceu_write(pcdev, CDOCR, value);
|
|
|
ceu_write(pcdev, CFWCR, 0); /* keep "datafetch firewall" disabled */
|
|
@@ -889,13 +887,19 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd,
|
|
|
{
|
|
|
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
|
|
|
struct sh_mobile_ceu_dev *pcdev = ici->priv;
|
|
|
- unsigned long camera_flags, common_flags;
|
|
|
+ struct v4l2_subdev *sd = find_bus_subdev(pcdev, icd);
|
|
|
+ unsigned long common_flags = CEU_BUS_FLAGS;
|
|
|
+ struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,};
|
|
|
+ int ret;
|
|
|
|
|
|
- camera_flags = icd->ops->query_bus_param(icd);
|
|
|
- common_flags = soc_camera_bus_param_compatible(camera_flags,
|
|
|
- make_bus_param(pcdev));
|
|
|
- if (!common_flags || buswidth > 16 ||
|
|
|
- (buswidth > 8 && !(common_flags & SOCAM_DATAWIDTH_16)))
|
|
|
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
|
|
|
+ if (!ret)
|
|
|
+ common_flags = soc_mbus_config_compatible(&cfg,
|
|
|
+ common_flags);
|
|
|
+ else if (ret != -ENOIOCTLCMD)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (!common_flags || buswidth > 16)
|
|
|
return -EINVAL;
|
|
|
|
|
|
return 0;
|
|
@@ -905,26 +909,26 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
|
|
|
{
|
|
|
.fourcc = V4L2_PIX_FMT_NV12,
|
|
|
.name = "NV12",
|
|
|
- .bits_per_sample = 12,
|
|
|
- .packing = SOC_MBUS_PACKING_NONE,
|
|
|
+ .bits_per_sample = 8,
|
|
|
+ .packing = SOC_MBUS_PACKING_1_5X8,
|
|
|
.order = SOC_MBUS_ORDER_LE,
|
|
|
}, {
|
|
|
.fourcc = V4L2_PIX_FMT_NV21,
|
|
|
.name = "NV21",
|
|
|
- .bits_per_sample = 12,
|
|
|
- .packing = SOC_MBUS_PACKING_NONE,
|
|
|
+ .bits_per_sample = 8,
|
|
|
+ .packing = SOC_MBUS_PACKING_1_5X8,
|
|
|
.order = SOC_MBUS_ORDER_LE,
|
|
|
}, {
|
|
|
.fourcc = V4L2_PIX_FMT_NV16,
|
|
|
.name = "NV16",
|
|
|
- .bits_per_sample = 16,
|
|
|
- .packing = SOC_MBUS_PACKING_NONE,
|
|
|
+ .bits_per_sample = 8,
|
|
|
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
|
|
|
.order = SOC_MBUS_ORDER_LE,
|
|
|
}, {
|
|
|
.fourcc = V4L2_PIX_FMT_NV61,
|
|
|
.name = "NV61",
|
|
|
- .bits_per_sample = 16,
|
|
|
- .packing = SOC_MBUS_PACKING_NONE,
|
|
|
+ .bits_per_sample = 8,
|
|
|
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
|
|
|
.order = SOC_MBUS_ORDER_LE,
|
|
|
},
|
|
|
};
|
|
@@ -933,6 +937,8 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = {
|
|
|
static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt)
|
|
|
{
|
|
|
return fmt->packing == SOC_MBUS_PACKING_NONE ||
|
|
|
+ (fmt->bits_per_sample == 8 &&
|
|
|
+ fmt->packing == SOC_MBUS_PACKING_1_5X8) ||
|
|
|
(fmt->bits_per_sample == 8 &&
|
|
|
fmt->packing == SOC_MBUS_PACKING_2X8_PADHI) ||
|
|
|
(fmt->bits_per_sample > 8 &&
|
|
@@ -966,6 +972,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
|
|
|
}
|
|
|
|
|
|
if (!pcdev->pdata->csi2) {
|
|
|
+ /* Are there any restrictions in the CSI-2 case? */
|
|
|
ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample);
|
|
|
if (ret < 0)
|
|
|
return 0;
|
|
@@ -1626,8 +1633,6 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
|
|
bool image_mode;
|
|
|
enum v4l2_field field;
|
|
|
|
|
|
- dev_geo(dev, "S_FMT(pix=0x%x, %ux%u)\n", pixfmt, pix->width, pix->height);
|
|
|
-
|
|
|
switch (pix->field) {
|
|
|
default:
|
|
|
pix->field = V4L2_FIELD_NONE;
|
|
@@ -1665,6 +1670,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
|
|
|
image_mode = false;
|
|
|
}
|
|
|
|
|
|
+ dev_info(dev, "S_FMT(pix=0x%x, fld 0x%x, code 0x%x, %ux%u)\n", pixfmt, mf.field, mf.code,
|
|
|
+ pix->width, pix->height);
|
|
|
+
|
|
|
dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height);
|
|
|
|
|
|
/* 5. - 9. */
|