// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2023 Intel Corporation. All rights reserved. * Intel Visual Sensing Controller CSI Linux driver
*/
/* * To set ownership of CSI-2 link and to configure CSI-2 link, there * are specific commands, which are sent via MEI protocol. The send * command function uses "completion" as a synchronization mechanism. * The response for command is received via a mei callback which wakes * up the caller. There can be only one outstanding command at a time.
*/
/* the 5s used here is based on experiment */ #define CSI_CMD_TIMEOUT (5 * HZ) /* to setup CSI-2 link an extra delay needed and determined experimentally */ #define CSI_FW_READY_DELAY_MS 100 /* link frequency unit is 100kHz */ #define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ)))
/* * identify the command id supported by firmware * IPC, as well as the privacy notification id * used when processing privacy event.
*/ enum csi_cmd_id { /* used to set csi ownership */
CSI_SET_OWNER = 0,
/* used to configure CSI-2 link */
CSI_SET_CONF = 2,
/* privacy notification id used when privacy state changes */
CSI_PRIVACY_NOTIF = 6,
};
/* send a command to firmware and mutex must be held by caller */ staticint mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len)
{ struct csi_cmd *cmd = (struct csi_cmd *)buf; int ret;
reinit_completion(&csi->cmd_completion);
ret = mei_cldev_send(csi->cldev, buf, len); if (ret < 0) goto out;
ret = wait_for_completion_killable_timeout(&csi->cmd_completion,
CSI_CMD_TIMEOUT); if (ret < 0) { goto out;
} elseif (!ret) {
ret = -ETIMEDOUT; goto out;
}
/* command response status */
ret = csi->cmd_response.status; if (ret == -1) { /* notify privacy on instead of reporting error */
ret = 0;
v4l2_ctrl_s_ctrl(csi->privacy_ctrl, 1);
} elseif (ret) {
ret = -EINVAL; goto out;
}
if (csi->cmd_response.cmd_id != cmd->cmd_id)
ret = -EINVAL;
out: return ret;
}
/* set CSI-2 link ownership */ staticint csi_set_link_owner(struct mei_csi *csi, enum csi_link_owner owner)
{ struct csi_cmd cmd = { 0 };
size_t cmd_size; int ret;
ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size); /* * wait configuration ready if download success. placing * delay under mutex is to make sure current command flow * completed before starting a possible new one.
*/ if (!ret)
msleep(CSI_FW_READY_DELAY_MS);
/* switch CSI-2 link to IVSC */
ret = csi_set_link_owner(csi, CSI_LINK_IVSC); if (ret < 0)
dev_warn(&csi->cldev->dev, "failed to switch CSI2 link: %d\n", ret);
}
switch (format->format.code) { case MEDIA_BUS_FMT_RGB444_1X12: case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: case MEDIA_BUS_FMT_RGB565_1X16: case MEDIA_BUS_FMT_BGR565_2X8_BE: case MEDIA_BUS_FMT_BGR565_2X8_LE: case MEDIA_BUS_FMT_RGB565_2X8_BE: case MEDIA_BUS_FMT_RGB565_2X8_LE: case MEDIA_BUS_FMT_RGB666_1X18: case MEDIA_BUS_FMT_RBG888_1X24: case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: case MEDIA_BUS_FMT_BGR888_1X24: case MEDIA_BUS_FMT_GBR888_1X24: case MEDIA_BUS_FMT_RGB888_1X24: case MEDIA_BUS_FMT_RGB888_2X12_BE: case MEDIA_BUS_FMT_RGB888_2X12_LE: case MEDIA_BUS_FMT_ARGB8888_1X32: case MEDIA_BUS_FMT_RGB888_1X32_PADHI: case MEDIA_BUS_FMT_RGB101010_1X30: case MEDIA_BUS_FMT_RGB121212_1X36: case MEDIA_BUS_FMT_RGB161616_1X48: case MEDIA_BUS_FMT_Y8_1X8: case MEDIA_BUS_FMT_UV8_1X8: case MEDIA_BUS_FMT_UYVY8_1_5X8: case MEDIA_BUS_FMT_VYUY8_1_5X8: case MEDIA_BUS_FMT_YUYV8_1_5X8: case MEDIA_BUS_FMT_YVYU8_1_5X8: case MEDIA_BUS_FMT_UYVY8_2X8: case MEDIA_BUS_FMT_VYUY8_2X8: case MEDIA_BUS_FMT_YUYV8_2X8: case MEDIA_BUS_FMT_YVYU8_2X8: case MEDIA_BUS_FMT_Y10_1X10: case MEDIA_BUS_FMT_UYVY10_2X10: case MEDIA_BUS_FMT_VYUY10_2X10: case MEDIA_BUS_FMT_YUYV10_2X10: case MEDIA_BUS_FMT_YVYU10_2X10: case MEDIA_BUS_FMT_Y12_1X12: case MEDIA_BUS_FMT_UYVY12_2X12: case MEDIA_BUS_FMT_VYUY12_2X12: case MEDIA_BUS_FMT_YUYV12_2X12: case MEDIA_BUS_FMT_YVYU12_2X12: case MEDIA_BUS_FMT_UYVY8_1X16: case MEDIA_BUS_FMT_VYUY8_1X16: case MEDIA_BUS_FMT_YUYV8_1X16: case MEDIA_BUS_FMT_YVYU8_1X16: case MEDIA_BUS_FMT_YDYUYDYV8_1X16: case MEDIA_BUS_FMT_UYVY10_1X20: case MEDIA_BUS_FMT_VYUY10_1X20: case MEDIA_BUS_FMT_YUYV10_1X20: case MEDIA_BUS_FMT_YVYU10_1X20: case MEDIA_BUS_FMT_VUY8_1X24: case MEDIA_BUS_FMT_YUV8_1X24: case MEDIA_BUS_FMT_UYYVYY8_0_5X24: case MEDIA_BUS_FMT_UYVY12_1X24: case MEDIA_BUS_FMT_VYUY12_1X24: case MEDIA_BUS_FMT_YUYV12_1X24: case MEDIA_BUS_FMT_YVYU12_1X24: case MEDIA_BUS_FMT_YUV10_1X30: case MEDIA_BUS_FMT_UYYVYY10_0_5X30: case MEDIA_BUS_FMT_AYUV8_1X32: case MEDIA_BUS_FMT_UYYVYY12_0_5X36: case MEDIA_BUS_FMT_YUV12_1X36: case MEDIA_BUS_FMT_YUV16_1X48: case MEDIA_BUS_FMT_UYYVYY16_0_5X48: case MEDIA_BUS_FMT_JPEG_1X8: case MEDIA_BUS_FMT_AHSV8888_1X32: case MEDIA_BUS_FMT_SBGGR8_1X8: case MEDIA_BUS_FMT_SGBRG8_1X8: case MEDIA_BUS_FMT_SGRBG8_1X8: case MEDIA_BUS_FMT_SRGGB8_1X8: case MEDIA_BUS_FMT_SBGGR10_1X10: case MEDIA_BUS_FMT_SGBRG10_1X10: case MEDIA_BUS_FMT_SGRBG10_1X10: case MEDIA_BUS_FMT_SRGGB10_1X10: case MEDIA_BUS_FMT_SBGGR12_1X12: case MEDIA_BUS_FMT_SGBRG12_1X12: case MEDIA_BUS_FMT_SGRBG12_1X12: case MEDIA_BUS_FMT_SRGGB12_1X12: case MEDIA_BUS_FMT_SBGGR14_1X14: case MEDIA_BUS_FMT_SGBRG14_1X14: case MEDIA_BUS_FMT_SGRBG14_1X14: case MEDIA_BUS_FMT_SRGGB14_1X14: case MEDIA_BUS_FMT_SBGGR16_1X16: case MEDIA_BUS_FMT_SGBRG16_1X16: case MEDIA_BUS_FMT_SGRBG16_1X16: case MEDIA_BUS_FMT_SRGGB16_1X16: break; default:
format->format.code = MEDIA_BUS_FMT_Y8_1X8; break;
}
if (format->format.field == V4L2_FIELD_ANY)
format->format.field = V4L2_FIELD_NONE;
mbus_config->type = V4L2_MBUS_CSI2_DPHY; for (i = 0; i < V4L2_MBUS_CSI2_MAX_DATA_LANES; i++)
mbus_config->bus.mipi_csi2.data_lanes[i] = i + 1;
mbus_config->bus.mipi_csi2.num_data_lanes = csi->nr_of_lanes;
source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0); if (!source_ep) {
ret = -ENOTCONN;
dev_err(dev, "can't obtain source endpoint\n"); goto out_nf_cleanup;
}
ret = v4l2_fwnode_endpoint_parse(source_ep, &v4l2_ep);
fwnode_handle_put(source_ep); if (ret) {
dev_err(dev, "could not parse v4l2 source endpoint\n"); goto out_nf_cleanup;
}
if (csi->nr_of_lanes != v4l2_ep.bus.mipi_csi2.num_data_lanes) {
ret = -EINVAL;
dev_err(dev, "the number of lanes does not match (%u vs. %u)\n",
csi->nr_of_lanes, v4l2_ep.bus.mipi_csi2.num_data_lanes); goto out_nf_cleanup;
}
asd = v4l2_async_nf_add_fwnode_remote(&csi->notifier, sink_ep, struct v4l2_async_connection); if (IS_ERR(asd)) {
ret = PTR_ERR(asd); goto out_nf_cleanup;
}
ret = v4l2_async_nf_register(&csi->notifier); if (ret) goto out_nf_cleanup;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.