diff options
Diffstat (limited to 'drivers/media/platform/ti-vpe/cal-camerarx.c')
-rw-r--r-- | drivers/media/platform/ti-vpe/cal-camerarx.c | 247 |
1 files changed, 145 insertions, 102 deletions
diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti-vpe/cal-camerarx.c index 124a4e2bdefe..4bf7a8c2e711 100644 --- a/drivers/media/platform/ti-vpe/cal-camerarx.c +++ b/drivers/media/platform/ti-vpe/cal-camerarx.c @@ -45,22 +45,30 @@ static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val) * ------------------------------------------------------------------ */ -static s64 cal_camerarx_get_external_rate(struct cal_camerarx *phy) +static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy) { - struct v4l2_ctrl *ctrl; - s64 rate; - - ctrl = v4l2_ctrl_find(phy->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - phy_err(phy, "no pixel rate control in subdev: %s\n", - phy->sensor->name); - return -EPIPE; + struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2; + u32 num_lanes = mipi_csi2->num_data_lanes; + const struct cal_format_info *fmtinfo; + u32 bpp; + s64 freq; + + fmtinfo = cal_format_by_code(phy->formats[CAL_CAMERARX_PAD_SINK].code); + if (!fmtinfo) + return -EINVAL; + + bpp = fmtinfo->bpp; + + freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes); + if (freq < 0) { + phy_err(phy, "failed to get link freq for subdev '%s'\n", + phy->source->name); + return freq; } - rate = v4l2_ctrl_g_ctrl_int64(ctrl); - phy_dbg(3, phy, "sensor Pixel Rate: %llu\n", rate); + phy_dbg(3, phy, "Source Link Freq: %llu\n", freq); - return rate; + return freq; } static void cal_camerarx_lane_config(struct cal_camerarx *phy) @@ -116,34 +124,19 @@ void cal_camerarx_disable(struct cal_camerarx *phy) #define TCLK_MISS 1 #define TCLK_SETTLE 14 -static void cal_camerarx_config(struct cal_camerarx *phy, s64 external_rate) +static void cal_camerarx_config(struct cal_camerarx *phy, s64 link_freq) { unsigned int reg0, reg1; unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &phy->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; /* DPHY timing configuration */ - /* - * CSI-2 is DDR and we only count used lanes. - * - * csi2_ddrclk_khz = external_rate / 1000 - * / (2 * num_lanes) * phy->fmtinfo->bpp; - */ - csi2_ddrclk_khz = div_s64(external_rate * phy->fmtinfo->bpp, - 2 * num_lanes * 1000); - - phy_dbg(1, phy, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; + ths_term = div_s64(20 * link_freq, 1000 * 1000 * 1000); phy_dbg(1, phy, "ths_term: %d (0x%02x)\n", ths_term, ths_term); /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; + ths_settle = div_s64(105 * link_freq, 1000 * 1000 * 1000) + 4; phy_dbg(1, phy, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); reg0 = camerarx_read(phy, CAL_CSI2_PHY_REG0); @@ -240,25 +233,42 @@ static void cal_camerarx_enable_irqs(struct cal_camerarx *phy) CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error IRQs. */ + const u32 vc_err_mask = + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(0) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(1) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(2) | + CAL_CSI2_VC_IRQ_CS_IRQ_MASK(3) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(0) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(1) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(2) | + CAL_CSI2_VC_IRQ_ECC_CORRECTION_IRQ_MASK(3); + + /* Enable CIO & VC error IRQs. */ cal_write(phy->cal, CAL_HL_IRQENABLE_SET(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); + CAL_HL_IRQ_CIO_MASK(phy->instance) | + CAL_HL_IRQ_VC_MASK(phy->instance)); cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), cio_err_mask); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), + vc_err_mask); } static void cal_camerarx_disable_irqs(struct cal_camerarx *phy) { /* Disable CIO error irqs */ cal_write(phy->cal, CAL_HL_IRQENABLE_CLR(0), - CAL_HL_IRQ_CIO_MASK(phy->instance)); + CAL_HL_IRQ_CIO_MASK(phy->instance) | + CAL_HL_IRQ_VC_MASK(phy->instance)); cal_write(phy->cal, CAL_CSI2_COMPLEXIO_IRQENABLE(phy->instance), 0); + cal_write(phy->cal, CAL_CSI2_VC_IRQENABLE(phy->instance), 0); } static void cal_camerarx_ppi_enable(struct cal_camerarx *phy) { cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), + 1, CAL_CSI2_PPI_CTRL_ECC_EN_MASK); + + cal_write_field(phy->cal, CAL_CSI2_PPI_CTRL(phy->instance), 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); } @@ -270,16 +280,21 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy) static int cal_camerarx_start(struct cal_camerarx *phy) { - s64 external_rate; + s64 link_freq; u32 sscounter; u32 val; int ret; - external_rate = cal_camerarx_get_external_rate(phy); - if (external_rate < 0) - return external_rate; + if (phy->enable_count > 0) { + phy->enable_count++; + return 0; + } + + link_freq = cal_camerarx_get_ext_link_freq(phy); + if (link_freq < 0) + return link_freq; - ret = v4l2_subdev_call(phy->sensor, core, s_power, 1); + ret = v4l2_subdev_call(phy->source, core, s_power, 1); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { phy_err(phy, "power on failed in subdev\n"); return ret; @@ -311,7 +326,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy) * 2. CSI PHY and link initialization sequence. * * a. Deassert the CSI-2 PHY reset. Do not wait for reset completion - * at this point, as it requires the external sensor to send the + * at this point, as it requires the external source to send the * CSI-2 HS clock. */ cal_write_field(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance), @@ -325,7 +340,7 @@ static int cal_camerarx_start(struct cal_camerarx *phy) camerarx_read(phy, CAL_CSI2_PHY_REG0); /* Program the PHY timing parameters. */ - cal_camerarx_config(phy, external_rate); + cal_camerarx_config(phy, link_freq); /* * b. Assert the FORCERXMODE signal. @@ -370,12 +385,12 @@ static int cal_camerarx_start(struct cal_camerarx *phy) cal_camerarx_power(phy, true); /* - * Start the sensor to enable the CSI-2 HS clock. We can now wait for + * Start the source to enable the CSI-2 HS clock. We can now wait for * CSI-2 PHY reset to complete. */ - ret = v4l2_subdev_call(phy->sensor, video, s_stream, 1); + ret = v4l2_subdev_call(phy->source, video, s_stream, 1); if (ret) { - v4l2_subdev_call(phy->sensor, core, s_power, 0); + v4l2_subdev_call(phy->source, core, s_power, 0); cal_camerarx_disable_irqs(phy); phy_err(phy, "stream on failed in subdev\n"); return ret; @@ -399,14 +414,18 @@ static int cal_camerarx_start(struct cal_camerarx *phy) /* Finally, enable the PHY Protocol Interface (PPI). */ cal_camerarx_ppi_enable(phy); + phy->enable_count++; + return 0; } static void cal_camerarx_stop(struct cal_camerarx *phy) { - unsigned int i; int ret; + if (--phy->enable_count > 0) + return; + cal_camerarx_ppi_disable(phy); cal_camerarx_disable_irqs(phy); @@ -418,27 +437,17 @@ static void cal_camerarx_stop(struct cal_camerarx *phy) CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (cal_read_field(phy->cal, - CAL_CSI2_COMPLEXIO_CFG(phy->instance), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) - break; - usleep_range(1000, 1100); - } - phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", + phy_dbg(3, phy, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset\n", phy->instance, - cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance)), i, - (i >= 10) ? "(timeout)" : ""); + cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance))); /* Disable the phy */ cal_camerarx_disable(phy); - if (v4l2_subdev_call(phy->sensor, video, s_stream, 0)) + if (v4l2_subdev_call(phy->source, video, s_stream, 0)) phy_err(phy, "stream off failed in subdev\n"); - ret = v4l2_subdev_call(phy->sensor, core, s_power, 0); + ret = v4l2_subdev_call(phy->source, core, s_power, 0); if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) phy_err(phy, "power off failed in subdev\n"); } @@ -558,16 +567,16 @@ static int cal_camerarx_parse_dt(struct cal_camerarx *phy) endpoint->bus.mipi_csi2.flags); /* Retrieve the connected device and store it for later use. */ - phy->sensor_ep_node = of_graph_get_remote_endpoint(ep_node); - phy->sensor_node = of_graph_get_port_parent(phy->sensor_ep_node); - if (!phy->sensor_node) { + phy->source_ep_node = of_graph_get_remote_endpoint(ep_node); + phy->source_node = of_graph_get_port_parent(phy->source_ep_node); + if (!phy->source_node) { phy_dbg(3, phy, "Can't get remote parent\n"); - of_node_put(phy->sensor_ep_node); + of_node_put(phy->source_ep_node); ret = -EINVAL; goto done; } - phy_dbg(1, phy, "Found connected device %pOFn\n", phy->sensor_node); + phy_dbg(1, phy, "Found connected device %pOFn\n", phy->source_node); done: of_node_put(ep_node); @@ -602,12 +611,18 @@ cal_camerarx_get_pad_format(struct cal_camerarx *phy, static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable) { struct cal_camerarx *phy = to_cal_camerarx(sd); + int ret = 0; + + mutex_lock(&phy->mutex); if (enable) - return cal_camerarx_start(phy); + ret = cal_camerarx_start(phy); + else + cal_camerarx_stop(phy); - cal_camerarx_stop(phy); - return 0; + mutex_unlock(&phy->mutex); + + return ret; } static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, @@ -615,27 +630,36 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_mbus_code_enum *code) { struct cal_camerarx *phy = to_cal_camerarx(sd); + int ret = 0; + + mutex_lock(&phy->mutex); /* No transcoding, source and sink codes must match. */ - if (code->pad == CAL_CAMERARX_PAD_SOURCE) { + if (cal_rx_pad_is_source(code->pad)) { struct v4l2_mbus_framefmt *fmt; - if (code->index > 0) - return -EINVAL; + if (code->index > 0) { + ret = -EINVAL; + goto out; + } fmt = cal_camerarx_get_pad_format(phy, sd_state, CAL_CAMERARX_PAD_SINK, code->which); code->code = fmt->code; - return 0; - } + } else { + if (code->index >= cal_num_formats) { + ret = -EINVAL; + goto out; + } - if (code->index >= cal_num_formats) - return -EINVAL; + code->code = cal_formats[code->index].code; + } - code->code = cal_formats[code->index].code; +out: + mutex_unlock(&phy->mutex); - return 0; + return ret; } static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, @@ -644,38 +668,46 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd, { struct cal_camerarx *phy = to_cal_camerarx(sd); const struct cal_format_info *fmtinfo; + int ret = 0; if (fse->index > 0) return -EINVAL; + mutex_lock(&phy->mutex); + /* No transcoding, source and sink formats must match. */ - if (fse->pad == CAL_CAMERARX_PAD_SOURCE) { + if (cal_rx_pad_is_source(fse->pad)) { struct v4l2_mbus_framefmt *fmt; fmt = cal_camerarx_get_pad_format(phy, sd_state, CAL_CAMERARX_PAD_SINK, fse->which); - if (fse->code != fmt->code) - return -EINVAL; + if (fse->code != fmt->code) { + ret = -EINVAL; + goto out; + } fse->min_width = fmt->width; fse->max_width = fmt->width; fse->min_height = fmt->height; fse->max_height = fmt->height; + } else { + fmtinfo = cal_format_by_code(fse->code); + if (!fmtinfo) { + ret = -EINVAL; + goto out; + } - return 0; + fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); + fse->min_height = CAL_MIN_HEIGHT_LINES; + fse->max_height = CAL_MAX_HEIGHT_LINES; } - fmtinfo = cal_format_by_code(fse->code); - if (!fmtinfo) - return -EINVAL; - - fse->min_width = CAL_MIN_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); - fse->max_width = CAL_MAX_WIDTH_BYTES * 8 / ALIGN(fmtinfo->bpp, 8); - fse->min_height = CAL_MIN_HEIGHT_LINES; - fse->max_height = CAL_MAX_HEIGHT_LINES; +out: + mutex_unlock(&phy->mutex); - return 0; + return ret; } static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, @@ -685,10 +717,14 @@ static int cal_camerarx_sd_get_fmt(struct v4l2_subdev *sd, struct cal_camerarx *phy = to_cal_camerarx(sd); struct v4l2_mbus_framefmt *fmt; + mutex_lock(&phy->mutex); + fmt = cal_camerarx_get_pad_format(phy, sd_state, format->pad, format->which); format->format = *fmt; + mutex_unlock(&phy->mutex); + return 0; } @@ -702,21 +738,18 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, unsigned int bpp; /* No transcoding, source and sink formats must match. */ - if (format->pad == CAL_CAMERARX_PAD_SOURCE) + if (cal_rx_pad_is_source(format->pad)) return cal_camerarx_sd_get_fmt(sd, sd_state, format); /* - * Default to the first format is the requested media bus code isn't + * Default to the first format if the requested media bus code isn't * supported. */ fmtinfo = cal_format_by_code(format->format.code); if (!fmtinfo) fmtinfo = &cal_formats[0]; - /* - * Clamp the size, update the code. The field and colorspace are - * accepted as-is. - */ + /* Clamp the size, update the code. The colorspace is accepted as-is. */ bpp = ALIGN(fmtinfo->bpp, 8); format->format.width = clamp_t(unsigned int, format->format.width, @@ -726,20 +759,23 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd, CAL_MIN_HEIGHT_LINES, CAL_MAX_HEIGHT_LINES); format->format.code = fmtinfo->code; + format->format.field = V4L2_FIELD_NONE; /* Store the format and propagate it to the source pad. */ + + mutex_lock(&phy->mutex); + fmt = cal_camerarx_get_pad_format(phy, sd_state, CAL_CAMERARX_PAD_SINK, format->which); *fmt = format->format; fmt = cal_camerarx_get_pad_format(phy, sd_state, - CAL_CAMERARX_PAD_SOURCE, + CAL_CAMERARX_PAD_FIRST_SOURCE, format->which); *fmt = format->format; - if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) - phy->fmtinfo = fmtinfo; + mutex_unlock(&phy->mutex); return 0; } @@ -798,6 +834,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, struct platform_device *pdev = to_platform_device(cal->dev); struct cal_camerarx *phy; struct v4l2_subdev *sd; + unsigned int i; int ret; phy = kzalloc(sizeof(*phy), GFP_KERNEL); @@ -807,6 +844,8 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, phy->cal = cal; phy->instance = instance; + mutex_init(&phy->mutex); + phy->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, (instance == 0) ? "cal_rx_core0" : @@ -838,14 +877,17 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal, sd->dev = cal->dev; phy->pads[CAL_CAMERARX_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - phy->pads[CAL_CAMERARX_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + for (i = CAL_CAMERARX_PAD_FIRST_SOURCE; i < CAL_CAMERARX_NUM_PADS; ++i) + phy->pads[i].flags = MEDIA_PAD_FL_SOURCE; sd->entity.ops = &cal_camerarx_media_ops; ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(phy->pads), phy->pads); if (ret) goto error; - cal_camerarx_sd_init_cfg(sd, NULL); + ret = cal_camerarx_sd_init_cfg(sd, NULL); + if (ret) + goto error; ret = v4l2_device_register_subdev(&cal->v4l2_dev, sd); if (ret) @@ -866,7 +908,8 @@ void cal_camerarx_destroy(struct cal_camerarx *phy) v4l2_device_unregister_subdev(&phy->subdev); media_entity_cleanup(&phy->subdev.entity); - of_node_put(phy->sensor_ep_node); - of_node_put(phy->sensor_node); + of_node_put(phy->source_ep_node); + of_node_put(phy->source_node); + mutex_destroy(&phy->mutex); kfree(phy); } |