|
@@ -243,25 +243,95 @@
|
|
|
|
|
|
#define SCALER_RATIO_MAX 16
|
|
|
|
|
|
-static const u32 vfe_formats[] = {
|
|
|
- MEDIA_BUS_FMT_UYVY8_2X8,
|
|
|
- MEDIA_BUS_FMT_VYUY8_2X8,
|
|
|
- MEDIA_BUS_FMT_YUYV8_2X8,
|
|
|
- MEDIA_BUS_FMT_YVYU8_2X8,
|
|
|
- MEDIA_BUS_FMT_SBGGR8_1X8,
|
|
|
- MEDIA_BUS_FMT_SGBRG8_1X8,
|
|
|
- MEDIA_BUS_FMT_SGRBG8_1X8,
|
|
|
- MEDIA_BUS_FMT_SRGGB8_1X8,
|
|
|
- MEDIA_BUS_FMT_SBGGR10_1X10,
|
|
|
- MEDIA_BUS_FMT_SGBRG10_1X10,
|
|
|
- MEDIA_BUS_FMT_SGRBG10_1X10,
|
|
|
- MEDIA_BUS_FMT_SRGGB10_1X10,
|
|
|
- MEDIA_BUS_FMT_SBGGR12_1X12,
|
|
|
- MEDIA_BUS_FMT_SGBRG12_1X12,
|
|
|
- MEDIA_BUS_FMT_SGRBG12_1X12,
|
|
|
- MEDIA_BUS_FMT_SRGGB12_1X12,
|
|
|
+static const struct {
|
|
|
+ u32 code;
|
|
|
+ u8 bpp;
|
|
|
+} vfe_formats[] = {
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_UYVY8_2X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_VYUY8_2X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_YUYV8_2X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_YVYU8_2X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SBGGR8_1X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SGBRG8_1X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SGRBG8_1X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SRGGB8_1X8,
|
|
|
+ 8,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SBGGR10_1X10,
|
|
|
+ 10,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SGBRG10_1X10,
|
|
|
+ 10,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SGRBG10_1X10,
|
|
|
+ 10,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SRGGB10_1X10,
|
|
|
+ 10,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SBGGR12_1X12,
|
|
|
+ 12,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SGBRG12_1X12,
|
|
|
+ 12,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SGRBG12_1X12,
|
|
|
+ 12,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ MEDIA_BUS_FMT_SRGGB12_1X12,
|
|
|
+ 12,
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * vfe_get_bpp - map media bus format to bits per pixel
|
|
|
+ * @code: media bus format code
|
|
|
+ *
|
|
|
+ * Return number of bits per pixel
|
|
|
+ */
|
|
|
+static u8 vfe_get_bpp(u32 code)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
|
|
|
+ if (code == vfe_formats[i].code)
|
|
|
+ return vfe_formats[i].bpp;
|
|
|
+
|
|
|
+ WARN(1, "Unknown format\n");
|
|
|
+
|
|
|
+ return vfe_formats[0].bpp;
|
|
|
+}
|
|
|
+
|
|
|
static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
|
|
|
{
|
|
|
u32 bits = readl_relaxed(vfe->base + reg);
|
|
@@ -1766,6 +1836,138 @@ static irqreturn_t vfe_isr(int irq, void *dev)
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * vfe_set_clock_rates - Calculate and set clock rates on VFE module
|
|
|
+ * @vfe: VFE device
|
|
|
+ *
|
|
|
+ * Return 0 on success or a negative error code otherwise
|
|
|
+ */
|
|
|
+static int vfe_set_clock_rates(struct vfe_device *vfe)
|
|
|
+{
|
|
|
+ struct device *dev = to_device(vfe);
|
|
|
+ u32 pixel_clock[MSM_VFE_LINE_NUM];
|
|
|
+ int i, j;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
|
|
|
+ ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
|
|
|
+ &pixel_clock[i]);
|
|
|
+ if (ret)
|
|
|
+ pixel_clock[i] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < vfe->nclocks; i++) {
|
|
|
+ struct camss_clock *clock = &vfe->clock[i];
|
|
|
+
|
|
|
+ if (!strcmp(clock->name, "camss_vfe_vfe")) {
|
|
|
+ u64 min_rate = 0;
|
|
|
+ long rate;
|
|
|
+
|
|
|
+ for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
|
|
|
+ u32 tmp;
|
|
|
+ u8 bpp;
|
|
|
+
|
|
|
+ if (j == VFE_LINE_PIX) {
|
|
|
+ tmp = pixel_clock[j];
|
|
|
+ } else {
|
|
|
+ bpp = vfe_get_bpp(vfe->line[j].
|
|
|
+ fmt[MSM_VFE_PAD_SINK].code);
|
|
|
+ tmp = pixel_clock[j] * bpp / 64;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (min_rate < tmp)
|
|
|
+ min_rate = tmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ camss_add_clock_margin(&min_rate);
|
|
|
+
|
|
|
+ for (j = 0; j < clock->nfreqs; j++)
|
|
|
+ if (min_rate < clock->freq[j])
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (j == clock->nfreqs) {
|
|
|
+ dev_err(dev,
|
|
|
+ "Pixel clock is too high for VFE");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if sensor pixel clock is not available */
|
|
|
+ /* set highest possible VFE clock rate */
|
|
|
+ if (min_rate == 0)
|
|
|
+ j = clock->nfreqs - 1;
|
|
|
+
|
|
|
+ rate = clk_round_rate(clock->clk, clock->freq[j]);
|
|
|
+ if (rate < 0) {
|
|
|
+ dev_err(dev, "clk round rate failed: %ld\n",
|
|
|
+ rate);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = clk_set_rate(clock->clk, rate);
|
|
|
+ if (ret < 0) {
|
|
|
+ dev_err(dev, "clk set rate failed: %d\n", ret);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * vfe_check_clock_rates - Check current clock rates on VFE module
|
|
|
+ * @vfe: VFE device
|
|
|
+ *
|
|
|
+ * Return 0 if current clock rates are suitable for a new pipeline
|
|
|
+ * or a negative error code otherwise
|
|
|
+ */
|
|
|
+static int vfe_check_clock_rates(struct vfe_device *vfe)
|
|
|
+{
|
|
|
+ u32 pixel_clock[MSM_VFE_LINE_NUM];
|
|
|
+ int i, j;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
|
|
|
+ ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
|
|
|
+ &pixel_clock[i]);
|
|
|
+ if (ret)
|
|
|
+ pixel_clock[i] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < vfe->nclocks; i++) {
|
|
|
+ struct camss_clock *clock = &vfe->clock[i];
|
|
|
+
|
|
|
+ if (!strcmp(clock->name, "camss_vfe_vfe")) {
|
|
|
+ u64 min_rate = 0;
|
|
|
+ unsigned long rate;
|
|
|
+
|
|
|
+ for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) {
|
|
|
+ u32 tmp;
|
|
|
+ u8 bpp;
|
|
|
+
|
|
|
+ if (j == VFE_LINE_PIX) {
|
|
|
+ tmp = pixel_clock[j];
|
|
|
+ } else {
|
|
|
+ bpp = vfe_get_bpp(vfe->line[j].
|
|
|
+ fmt[MSM_VFE_PAD_SINK].code);
|
|
|
+ tmp = pixel_clock[j] * bpp / 64;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (min_rate < tmp)
|
|
|
+ min_rate = tmp;
|
|
|
+ }
|
|
|
+
|
|
|
+ camss_add_clock_margin(&min_rate);
|
|
|
+
|
|
|
+ rate = clk_get_rate(clock->clk);
|
|
|
+ if (rate < min_rate)
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* vfe_get - Power up and reset VFE module
|
|
|
* @vfe: VFE Device
|
|
@@ -1779,6 +1981,10 @@ static int vfe_get(struct vfe_device *vfe)
|
|
|
mutex_lock(&vfe->power_lock);
|
|
|
|
|
|
if (vfe->power_count == 0) {
|
|
|
+ ret = vfe_set_clock_rates(vfe);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error_clocks;
|
|
|
+
|
|
|
ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
|
|
|
to_device(vfe));
|
|
|
if (ret < 0)
|
|
@@ -1791,6 +1997,10 @@ static int vfe_get(struct vfe_device *vfe)
|
|
|
vfe_reset_output_maps(vfe);
|
|
|
|
|
|
vfe_init_outputs(vfe);
|
|
|
+ } else {
|
|
|
+ ret = vfe_check_clock_rates(vfe);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error_clocks;
|
|
|
}
|
|
|
vfe->power_count++;
|
|
|
|
|
@@ -2074,7 +2284,7 @@ static void vfe_try_format(struct vfe_line *line,
|
|
|
/* Set format on sink pad */
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
|
|
|
- if (fmt->code == vfe_formats[i])
|
|
|
+ if (fmt->code == vfe_formats[i].code)
|
|
|
break;
|
|
|
|
|
|
/* If not found, use UYVY as default */
|
|
@@ -2241,7 +2451,7 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
|
|
|
if (code->index >= ARRAY_SIZE(vfe_formats))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- code->code = vfe_formats[code->index];
|
|
|
+ code->code = vfe_formats[code->index].code;
|
|
|
} else {
|
|
|
if (code->index > 0)
|
|
|
return -EINVAL;
|
|
@@ -2544,7 +2754,7 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
struct resource *r;
|
|
|
struct camss *camss = to_camss(vfe);
|
|
|
- int i;
|
|
|
+ int i, j;
|
|
|
int ret;
|
|
|
|
|
|
/* Memory */
|
|
@@ -2587,23 +2797,30 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
for (i = 0; i < vfe->nclocks; i++) {
|
|
|
- vfe->clock[i] = devm_clk_get(dev, res->clock[i]);
|
|
|
- if (IS_ERR(vfe->clock[i]))
|
|
|
- return PTR_ERR(vfe->clock[i]);
|
|
|
-
|
|
|
- if (res->clock_rate[i]) {
|
|
|
- long clk_rate = clk_round_rate(vfe->clock[i],
|
|
|
- res->clock_rate[i]);
|
|
|
- if (clk_rate < 0) {
|
|
|
- dev_err(dev, "clk round rate failed\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- ret = clk_set_rate(vfe->clock[i], clk_rate);
|
|
|
- if (ret < 0) {
|
|
|
- dev_err(dev, "clk set rate failed\n");
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ struct camss_clock *clock = &vfe->clock[i];
|
|
|
+
|
|
|
+ clock->clk = devm_clk_get(dev, res->clock[i]);
|
|
|
+ if (IS_ERR(clock->clk))
|
|
|
+ return PTR_ERR(clock->clk);
|
|
|
+
|
|
|
+ clock->name = res->clock[i];
|
|
|
+
|
|
|
+ clock->nfreqs = 0;
|
|
|
+ while (res->clock_rate[i][clock->nfreqs])
|
|
|
+ clock->nfreqs++;
|
|
|
+
|
|
|
+ if (!clock->nfreqs) {
|
|
|
+ clock->freq = NULL;
|
|
|
+ continue;
|
|
|
}
|
|
|
+
|
|
|
+ clock->freq = devm_kzalloc(dev, clock->nfreqs *
|
|
|
+ sizeof(*clock->freq), GFP_KERNEL);
|
|
|
+ if (!clock->freq)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ for (j = 0; j < clock->nfreqs; j++)
|
|
|
+ clock->freq[j] = res->clock_rate[i][j];
|
|
|
}
|
|
|
|
|
|
mutex_init(&vfe->power_lock);
|