162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2023 Intel Corporation. All rights reserved. 462306a36Sopenharmony_ci * Intel Visual Sensing Controller CSI Linux driver 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci * To set ownership of CSI-2 link and to configure CSI-2 link, there 962306a36Sopenharmony_ci * are specific commands, which are sent via MEI protocol. The send 1062306a36Sopenharmony_ci * command function uses "completion" as a synchronization mechanism. 1162306a36Sopenharmony_ci * The response for command is received via a mei callback which wakes 1262306a36Sopenharmony_ci * up the caller. There can be only one outstanding command at a time. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/completion.h> 1662306a36Sopenharmony_ci#include <linux/delay.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/math64.h> 1962306a36Sopenharmony_ci#include <linux/mei_cl_bus.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/mutex.h> 2262306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/units.h> 2562306a36Sopenharmony_ci#include <linux/uuid.h> 2662306a36Sopenharmony_ci#include <linux/workqueue.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <media/v4l2-async.h> 2962306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 3062306a36Sopenharmony_ci#include <media/v4l2-fwnode.h> 3162306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define MEI_CSI_DRIVER_NAME "ivsc_csi" 3462306a36Sopenharmony_ci#define MEI_CSI_ENTITY_NAME "Intel IVSC CSI" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define MEI_CSI_LINK_FREQ_400MHZ 400000000ULL 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* the 5s used here is based on experiment */ 3962306a36Sopenharmony_ci#define CSI_CMD_TIMEOUT (5 * HZ) 4062306a36Sopenharmony_ci/* to setup CSI-2 link an extra delay needed and determined experimentally */ 4162306a36Sopenharmony_ci#define CSI_FW_READY_DELAY_MS 100 4262306a36Sopenharmony_ci/* link frequency unit is 100kHz */ 4362306a36Sopenharmony_ci#define CSI_LINK_FREQ(x) ((u32)(div_u64(x, 100 * HZ_PER_KHZ))) 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * identify the command id supported by firmware 4762306a36Sopenharmony_ci * IPC, as well as the privacy notification id 4862306a36Sopenharmony_ci * used when processing privacy event. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cienum csi_cmd_id { 5162306a36Sopenharmony_ci /* used to set csi ownership */ 5262306a36Sopenharmony_ci CSI_SET_OWNER = 0, 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* used to configure CSI-2 link */ 5562306a36Sopenharmony_ci CSI_SET_CONF = 2, 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* privacy notification id used when privacy state changes */ 5862306a36Sopenharmony_ci CSI_PRIVACY_NOTIF = 6, 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* CSI-2 link ownership definition */ 6262306a36Sopenharmony_cienum csi_link_owner { 6362306a36Sopenharmony_ci CSI_LINK_IVSC, 6462306a36Sopenharmony_ci CSI_LINK_HOST, 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* privacy status definition */ 6862306a36Sopenharmony_cienum ivsc_privacy_status { 6962306a36Sopenharmony_ci CSI_PRIVACY_OFF, 7062306a36Sopenharmony_ci CSI_PRIVACY_ON, 7162306a36Sopenharmony_ci CSI_PRIVACY_MAX, 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cienum csi_pads { 7562306a36Sopenharmony_ci CSI_PAD_SINK, 7662306a36Sopenharmony_ci CSI_PAD_SOURCE, 7762306a36Sopenharmony_ci CSI_NUM_PADS 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* configuration of the CSI-2 link between host and IVSC */ 8162306a36Sopenharmony_cistruct csi_link_cfg { 8262306a36Sopenharmony_ci /* number of data lanes used on the CSI-2 link */ 8362306a36Sopenharmony_ci u32 nr_of_lanes; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* frequency of the CSI-2 link */ 8662306a36Sopenharmony_ci u32 link_freq; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* for future use */ 8962306a36Sopenharmony_ci u32 rsvd[2]; 9062306a36Sopenharmony_ci} __packed; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* CSI command structure */ 9362306a36Sopenharmony_cistruct csi_cmd { 9462306a36Sopenharmony_ci u32 cmd_id; 9562306a36Sopenharmony_ci union _cmd_param { 9662306a36Sopenharmony_ci u32 param; 9762306a36Sopenharmony_ci struct csi_link_cfg conf; 9862306a36Sopenharmony_ci } param; 9962306a36Sopenharmony_ci} __packed; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* CSI notification structure */ 10262306a36Sopenharmony_cistruct csi_notif { 10362306a36Sopenharmony_ci u32 cmd_id; 10462306a36Sopenharmony_ci int status; 10562306a36Sopenharmony_ci union _resp_cont { 10662306a36Sopenharmony_ci u32 cont; 10762306a36Sopenharmony_ci struct csi_link_cfg conf; 10862306a36Sopenharmony_ci } cont; 10962306a36Sopenharmony_ci} __packed; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistruct mei_csi { 11262306a36Sopenharmony_ci struct mei_cl_device *cldev; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* command response */ 11562306a36Sopenharmony_ci struct csi_notif cmd_response; 11662306a36Sopenharmony_ci /* used to wait for command response from firmware */ 11762306a36Sopenharmony_ci struct completion cmd_completion; 11862306a36Sopenharmony_ci /* protect command download */ 11962306a36Sopenharmony_ci struct mutex lock; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci struct v4l2_subdev subdev; 12262306a36Sopenharmony_ci struct v4l2_subdev *remote; 12362306a36Sopenharmony_ci struct v4l2_async_notifier notifier; 12462306a36Sopenharmony_ci struct v4l2_ctrl_handler ctrl_handler; 12562306a36Sopenharmony_ci struct v4l2_ctrl *freq_ctrl; 12662306a36Sopenharmony_ci struct v4l2_ctrl *privacy_ctrl; 12762306a36Sopenharmony_ci unsigned int remote_pad; 12862306a36Sopenharmony_ci /* start streaming or not */ 12962306a36Sopenharmony_ci int streaming; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci struct media_pad pads[CSI_NUM_PADS]; 13262306a36Sopenharmony_ci struct v4l2_mbus_framefmt format_mbus[CSI_NUM_PADS]; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* number of data lanes used on the CSI-2 link */ 13562306a36Sopenharmony_ci u32 nr_of_lanes; 13662306a36Sopenharmony_ci /* frequency of the CSI-2 link */ 13762306a36Sopenharmony_ci u64 link_freq; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* privacy status */ 14062306a36Sopenharmony_ci enum ivsc_privacy_status status; 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic const struct v4l2_mbus_framefmt mei_csi_format_mbus_default = { 14462306a36Sopenharmony_ci .width = 1, 14562306a36Sopenharmony_ci .height = 1, 14662306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_Y8_1X8, 14762306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic s64 link_freq_menu_items[] = { 15162306a36Sopenharmony_ci MEI_CSI_LINK_FREQ_400MHZ 15262306a36Sopenharmony_ci}; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic inline struct mei_csi *notifier_to_csi(struct v4l2_async_notifier *n) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return container_of(n, struct mei_csi, notifier); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic inline struct mei_csi *sd_to_csi(struct v4l2_subdev *sd) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci return container_of(sd, struct mei_csi, subdev); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic inline struct mei_csi *ctrl_to_csi(struct v4l2_ctrl *ctrl) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci return container_of(ctrl->handler, struct mei_csi, ctrl_handler); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* send a command to firmware and mutex must be held by caller */ 17062306a36Sopenharmony_cistatic int mei_csi_send(struct mei_csi *csi, u8 *buf, size_t len) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct csi_cmd *cmd = (struct csi_cmd *)buf; 17362306a36Sopenharmony_ci int ret; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci reinit_completion(&csi->cmd_completion); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = mei_cldev_send(csi->cldev, buf, len); 17862306a36Sopenharmony_ci if (ret < 0) 17962306a36Sopenharmony_ci goto out; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = wait_for_completion_killable_timeout(&csi->cmd_completion, 18262306a36Sopenharmony_ci CSI_CMD_TIMEOUT); 18362306a36Sopenharmony_ci if (ret < 0) { 18462306a36Sopenharmony_ci goto out; 18562306a36Sopenharmony_ci } else if (!ret) { 18662306a36Sopenharmony_ci ret = -ETIMEDOUT; 18762306a36Sopenharmony_ci goto out; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* command response status */ 19162306a36Sopenharmony_ci ret = csi->cmd_response.status; 19262306a36Sopenharmony_ci if (ret) { 19362306a36Sopenharmony_ci ret = -EINVAL; 19462306a36Sopenharmony_ci goto out; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (csi->cmd_response.cmd_id != cmd->cmd_id) 19862306a36Sopenharmony_ci ret = -EINVAL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciout: 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* set CSI-2 link ownership */ 20562306a36Sopenharmony_cistatic int csi_set_link_owner(struct mei_csi *csi, enum csi_link_owner owner) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct csi_cmd cmd = { 0 }; 20862306a36Sopenharmony_ci size_t cmd_size; 20962306a36Sopenharmony_ci int ret; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci cmd.cmd_id = CSI_SET_OWNER; 21262306a36Sopenharmony_ci cmd.param.param = owner; 21362306a36Sopenharmony_ci cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci mutex_lock(&csi->lock); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci mutex_unlock(&csi->lock); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return ret; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* configure CSI-2 link between host and IVSC */ 22562306a36Sopenharmony_cistatic int csi_set_link_cfg(struct mei_csi *csi) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct csi_cmd cmd = { 0 }; 22862306a36Sopenharmony_ci size_t cmd_size; 22962306a36Sopenharmony_ci int ret; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci cmd.cmd_id = CSI_SET_CONF; 23262306a36Sopenharmony_ci cmd.param.conf.nr_of_lanes = csi->nr_of_lanes; 23362306a36Sopenharmony_ci cmd.param.conf.link_freq = CSI_LINK_FREQ(csi->link_freq); 23462306a36Sopenharmony_ci cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci mutex_lock(&csi->lock); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = mei_csi_send(csi, (u8 *)&cmd, cmd_size); 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * wait configuration ready if download success. placing 24162306a36Sopenharmony_ci * delay under mutex is to make sure current command flow 24262306a36Sopenharmony_ci * completed before starting a possible new one. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci if (!ret) 24562306a36Sopenharmony_ci msleep(CSI_FW_READY_DELAY_MS); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci mutex_unlock(&csi->lock); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return ret; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* callback for receive */ 25362306a36Sopenharmony_cistatic void mei_csi_rx(struct mei_cl_device *cldev) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct mei_csi *csi = mei_cldev_get_drvdata(cldev); 25662306a36Sopenharmony_ci struct csi_notif notif = { 0 }; 25762306a36Sopenharmony_ci int ret; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ret = mei_cldev_recv(cldev, (u8 *)¬if, sizeof(notif)); 26062306a36Sopenharmony_ci if (ret < 0) { 26162306a36Sopenharmony_ci dev_err(&cldev->dev, "recv error: %d\n", ret); 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci switch (notif.cmd_id) { 26662306a36Sopenharmony_ci case CSI_PRIVACY_NOTIF: 26762306a36Sopenharmony_ci if (notif.cont.cont < CSI_PRIVACY_MAX) { 26862306a36Sopenharmony_ci csi->status = notif.cont.cont; 26962306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(csi->privacy_ctrl, csi->status); 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci case CSI_SET_OWNER: 27362306a36Sopenharmony_ci case CSI_SET_CONF: 27462306a36Sopenharmony_ci memcpy(&csi->cmd_response, ¬if, ret); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci complete(&csi->cmd_completion); 27762306a36Sopenharmony_ci break; 27862306a36Sopenharmony_ci default: 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int mei_csi_set_stream(struct v4l2_subdev *sd, int enable) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci struct mei_csi *csi = sd_to_csi(sd); 28662306a36Sopenharmony_ci s64 freq; 28762306a36Sopenharmony_ci int ret; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (enable && csi->streaming == 0) { 29062306a36Sopenharmony_ci freq = v4l2_get_link_freq(csi->remote->ctrl_handler, 0, 0); 29162306a36Sopenharmony_ci if (freq < 0) { 29262306a36Sopenharmony_ci dev_err(&csi->cldev->dev, 29362306a36Sopenharmony_ci "error %lld, invalid link_freq\n", freq); 29462306a36Sopenharmony_ci ret = freq; 29562306a36Sopenharmony_ci goto err; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci csi->link_freq = freq; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* switch CSI-2 link to host */ 30062306a36Sopenharmony_ci ret = csi_set_link_owner(csi, CSI_LINK_HOST); 30162306a36Sopenharmony_ci if (ret < 0) 30262306a36Sopenharmony_ci goto err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* configure CSI-2 link */ 30562306a36Sopenharmony_ci ret = csi_set_link_cfg(csi); 30662306a36Sopenharmony_ci if (ret < 0) 30762306a36Sopenharmony_ci goto err_switch; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ret = v4l2_subdev_call(csi->remote, video, s_stream, 1); 31062306a36Sopenharmony_ci if (ret) 31162306a36Sopenharmony_ci goto err_switch; 31262306a36Sopenharmony_ci } else if (!enable && csi->streaming == 1) { 31362306a36Sopenharmony_ci v4l2_subdev_call(csi->remote, video, s_stream, 0); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* switch CSI-2 link to IVSC */ 31662306a36Sopenharmony_ci ret = csi_set_link_owner(csi, CSI_LINK_IVSC); 31762306a36Sopenharmony_ci if (ret < 0) 31862306a36Sopenharmony_ci dev_warn(&csi->cldev->dev, 31962306a36Sopenharmony_ci "failed to switch CSI2 link: %d\n", ret); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci csi->streaming = enable; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cierr_switch: 32762306a36Sopenharmony_ci csi_set_link_owner(csi, CSI_LINK_IVSC); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cierr: 33062306a36Sopenharmony_ci return ret; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt * 33462306a36Sopenharmony_cimei_csi_get_pad_format(struct v4l2_subdev *sd, 33562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 33662306a36Sopenharmony_ci unsigned int pad, u32 which) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct mei_csi *csi = sd_to_csi(sd); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci switch (which) { 34162306a36Sopenharmony_ci case V4L2_SUBDEV_FORMAT_TRY: 34262306a36Sopenharmony_ci return v4l2_subdev_get_try_format(sd, sd_state, pad); 34362306a36Sopenharmony_ci case V4L2_SUBDEV_FORMAT_ACTIVE: 34462306a36Sopenharmony_ci return &csi->format_mbus[pad]; 34562306a36Sopenharmony_ci default: 34662306a36Sopenharmony_ci return NULL; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int mei_csi_init_cfg(struct v4l2_subdev *sd, 35162306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mbusformat; 35462306a36Sopenharmony_ci struct mei_csi *csi = sd_to_csi(sd); 35562306a36Sopenharmony_ci unsigned int i; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci mutex_lock(&csi->lock); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci for (i = 0; i < sd->entity.num_pads; i++) { 36062306a36Sopenharmony_ci mbusformat = v4l2_subdev_get_try_format(sd, sd_state, i); 36162306a36Sopenharmony_ci *mbusformat = mei_csi_format_mbus_default; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci mutex_unlock(&csi->lock); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return 0; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic int mei_csi_get_fmt(struct v4l2_subdev *sd, 37062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 37162306a36Sopenharmony_ci struct v4l2_subdev_format *format) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mbusformat; 37462306a36Sopenharmony_ci struct mei_csi *csi = sd_to_csi(sd); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci mutex_lock(&csi->lock); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad, 37962306a36Sopenharmony_ci format->which); 38062306a36Sopenharmony_ci if (mbusformat) 38162306a36Sopenharmony_ci format->format = *mbusformat; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci mutex_unlock(&csi->lock); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int mei_csi_set_fmt(struct v4l2_subdev *sd, 38962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 39062306a36Sopenharmony_ci struct v4l2_subdev_format *format) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *source_mbusformat; 39362306a36Sopenharmony_ci struct v4l2_mbus_framefmt *mbusformat; 39462306a36Sopenharmony_ci struct mei_csi *csi = sd_to_csi(sd); 39562306a36Sopenharmony_ci struct media_pad *pad; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci mbusformat = mei_csi_get_pad_format(sd, sd_state, format->pad, 39862306a36Sopenharmony_ci format->which); 39962306a36Sopenharmony_ci if (!mbusformat) 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci source_mbusformat = mei_csi_get_pad_format(sd, sd_state, CSI_PAD_SOURCE, 40362306a36Sopenharmony_ci format->which); 40462306a36Sopenharmony_ci if (!source_mbusformat) 40562306a36Sopenharmony_ci return -EINVAL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci v4l_bound_align_image(&format->format.width, 1, 65536, 0, 40862306a36Sopenharmony_ci &format->format.height, 1, 65536, 0, 0); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci switch (format->format.code) { 41162306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_1X12: 41262306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE: 41362306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE: 41462306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE: 41562306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE: 41662306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_1X16: 41762306a36Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_BE: 41862306a36Sopenharmony_ci case MEDIA_BUS_FMT_BGR565_2X8_LE: 41962306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_BE: 42062306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB565_2X8_LE: 42162306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X18: 42262306a36Sopenharmony_ci case MEDIA_BUS_FMT_RBG888_1X24: 42362306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB666_1X24_CPADHI: 42462306a36Sopenharmony_ci case MEDIA_BUS_FMT_BGR888_1X24: 42562306a36Sopenharmony_ci case MEDIA_BUS_FMT_GBR888_1X24: 42662306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X24: 42762306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_2X12_BE: 42862306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_2X12_LE: 42962306a36Sopenharmony_ci case MEDIA_BUS_FMT_ARGB8888_1X32: 43062306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB888_1X32_PADHI: 43162306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB101010_1X30: 43262306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB121212_1X36: 43362306a36Sopenharmony_ci case MEDIA_BUS_FMT_RGB161616_1X48: 43462306a36Sopenharmony_ci case MEDIA_BUS_FMT_Y8_1X8: 43562306a36Sopenharmony_ci case MEDIA_BUS_FMT_UV8_1X8: 43662306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1_5X8: 43762306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_1_5X8: 43862306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_1_5X8: 43962306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_1_5X8: 44062306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 44162306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 44262306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 44362306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 44462306a36Sopenharmony_ci case MEDIA_BUS_FMT_Y10_1X10: 44562306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY10_2X10: 44662306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY10_2X10: 44762306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV10_2X10: 44862306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU10_2X10: 44962306a36Sopenharmony_ci case MEDIA_BUS_FMT_Y12_1X12: 45062306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY12_2X12: 45162306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY12_2X12: 45262306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV12_2X12: 45362306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU12_2X12: 45462306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_1X16: 45562306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_1X16: 45662306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_1X16: 45762306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_1X16: 45862306a36Sopenharmony_ci case MEDIA_BUS_FMT_YDYUYDYV8_1X16: 45962306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY10_1X20: 46062306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY10_1X20: 46162306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV10_1X20: 46262306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU10_1X20: 46362306a36Sopenharmony_ci case MEDIA_BUS_FMT_VUY8_1X24: 46462306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUV8_1X24: 46562306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYYVYY8_0_5X24: 46662306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY12_1X24: 46762306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY12_1X24: 46862306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV12_1X24: 46962306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU12_1X24: 47062306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUV10_1X30: 47162306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYYVYY10_0_5X30: 47262306a36Sopenharmony_ci case MEDIA_BUS_FMT_AYUV8_1X32: 47362306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYYVYY12_0_5X36: 47462306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUV12_1X36: 47562306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUV16_1X48: 47662306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYYVYY16_0_5X48: 47762306a36Sopenharmony_ci case MEDIA_BUS_FMT_JPEG_1X8: 47862306a36Sopenharmony_ci case MEDIA_BUS_FMT_AHSV8888_1X32: 47962306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR8_1X8: 48062306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG8_1X8: 48162306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG8_1X8: 48262306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB8_1X8: 48362306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_1X10: 48462306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG10_1X10: 48562306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG10_1X10: 48662306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB10_1X10: 48762306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR12_1X12: 48862306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG12_1X12: 48962306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG12_1X12: 49062306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB12_1X12: 49162306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR14_1X14: 49262306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG14_1X14: 49362306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG14_1X14: 49462306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB14_1X14: 49562306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR16_1X16: 49662306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG16_1X16: 49762306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG16_1X16: 49862306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB16_1X16: 49962306a36Sopenharmony_ci break; 50062306a36Sopenharmony_ci default: 50162306a36Sopenharmony_ci format->format.code = MEDIA_BUS_FMT_Y8_1X8; 50262306a36Sopenharmony_ci break; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (format->format.field == V4L2_FIELD_ANY) 50662306a36Sopenharmony_ci format->format.field = V4L2_FIELD_NONE; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci mutex_lock(&csi->lock); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci pad = &csi->pads[format->pad]; 51162306a36Sopenharmony_ci if (pad->flags & MEDIA_PAD_FL_SOURCE) 51262306a36Sopenharmony_ci format->format = csi->format_mbus[CSI_PAD_SINK]; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci *mbusformat = format->format; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (pad->flags & MEDIA_PAD_FL_SINK) 51762306a36Sopenharmony_ci *source_mbusformat = format->format; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci mutex_unlock(&csi->lock); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int mei_csi_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci struct mei_csi *csi = ctrl_to_csi(ctrl); 52762306a36Sopenharmony_ci s64 freq; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (ctrl->id == V4L2_CID_LINK_FREQ) { 53062306a36Sopenharmony_ci if (!csi->remote) 53162306a36Sopenharmony_ci return -EINVAL; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci freq = v4l2_get_link_freq(csi->remote->ctrl_handler, 0, 0); 53462306a36Sopenharmony_ci if (freq < 0) { 53562306a36Sopenharmony_ci dev_err(&csi->cldev->dev, 53662306a36Sopenharmony_ci "error %lld, invalid link_freq\n", freq); 53762306a36Sopenharmony_ci return -EINVAL; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci link_freq_menu_items[0] = freq; 54162306a36Sopenharmony_ci ctrl->val = 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops mei_csi_ctrl_ops = { 55062306a36Sopenharmony_ci .g_volatile_ctrl = mei_csi_g_volatile_ctrl, 55162306a36Sopenharmony_ci}; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops mei_csi_video_ops = { 55462306a36Sopenharmony_ci .s_stream = mei_csi_set_stream, 55562306a36Sopenharmony_ci}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops mei_csi_pad_ops = { 55862306a36Sopenharmony_ci .init_cfg = mei_csi_init_cfg, 55962306a36Sopenharmony_ci .get_fmt = mei_csi_get_fmt, 56062306a36Sopenharmony_ci .set_fmt = mei_csi_set_fmt, 56162306a36Sopenharmony_ci}; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic const struct v4l2_subdev_ops mei_csi_subdev_ops = { 56462306a36Sopenharmony_ci .video = &mei_csi_video_ops, 56562306a36Sopenharmony_ci .pad = &mei_csi_pad_ops, 56662306a36Sopenharmony_ci}; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic const struct media_entity_operations mei_csi_entity_ops = { 56962306a36Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 57062306a36Sopenharmony_ci}; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int mei_csi_notify_bound(struct v4l2_async_notifier *notifier, 57362306a36Sopenharmony_ci struct v4l2_subdev *subdev, 57462306a36Sopenharmony_ci struct v4l2_async_connection *asd) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct mei_csi *csi = notifier_to_csi(notifier); 57762306a36Sopenharmony_ci int pad; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, 58062306a36Sopenharmony_ci MEDIA_PAD_FL_SOURCE); 58162306a36Sopenharmony_ci if (pad < 0) 58262306a36Sopenharmony_ci return pad; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci csi->remote = subdev; 58562306a36Sopenharmony_ci csi->remote_pad = pad; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return media_create_pad_link(&subdev->entity, pad, 58862306a36Sopenharmony_ci &csi->subdev.entity, CSI_PAD_SINK, 58962306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED | 59062306a36Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic void mei_csi_notify_unbind(struct v4l2_async_notifier *notifier, 59462306a36Sopenharmony_ci struct v4l2_subdev *subdev, 59562306a36Sopenharmony_ci struct v4l2_async_connection *asd) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci struct mei_csi *csi = notifier_to_csi(notifier); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci csi->remote = NULL; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic const struct v4l2_async_notifier_operations mei_csi_notify_ops = { 60362306a36Sopenharmony_ci .bound = mei_csi_notify_bound, 60462306a36Sopenharmony_ci .unbind = mei_csi_notify_unbind, 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int mei_csi_init_controls(struct mei_csi *csi) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci u32 max; 61062306a36Sopenharmony_ci int ret; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci ret = v4l2_ctrl_handler_init(&csi->ctrl_handler, 2); 61362306a36Sopenharmony_ci if (ret) 61462306a36Sopenharmony_ci return ret; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci csi->ctrl_handler.lock = &csi->lock; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci max = ARRAY_SIZE(link_freq_menu_items) - 1; 61962306a36Sopenharmony_ci csi->freq_ctrl = v4l2_ctrl_new_int_menu(&csi->ctrl_handler, 62062306a36Sopenharmony_ci &mei_csi_ctrl_ops, 62162306a36Sopenharmony_ci V4L2_CID_LINK_FREQ, 62262306a36Sopenharmony_ci max, 62362306a36Sopenharmony_ci 0, 62462306a36Sopenharmony_ci link_freq_menu_items); 62562306a36Sopenharmony_ci if (csi->freq_ctrl) 62662306a36Sopenharmony_ci csi->freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY | 62762306a36Sopenharmony_ci V4L2_CTRL_FLAG_VOLATILE; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci csi->privacy_ctrl = v4l2_ctrl_new_std(&csi->ctrl_handler, NULL, 63062306a36Sopenharmony_ci V4L2_CID_PRIVACY, 0, 1, 1, 0); 63162306a36Sopenharmony_ci if (csi->privacy_ctrl) 63262306a36Sopenharmony_ci csi->privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (csi->ctrl_handler.error) 63562306a36Sopenharmony_ci return csi->ctrl_handler.error; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci csi->subdev.ctrl_handler = &csi->ctrl_handler; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int mei_csi_parse_firmware(struct mei_csi *csi) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci struct v4l2_fwnode_endpoint v4l2_ep = { 64562306a36Sopenharmony_ci .bus_type = V4L2_MBUS_CSI2_DPHY, 64662306a36Sopenharmony_ci }; 64762306a36Sopenharmony_ci struct device *dev = &csi->cldev->dev; 64862306a36Sopenharmony_ci struct v4l2_async_connection *asd; 64962306a36Sopenharmony_ci struct fwnode_handle *fwnode; 65062306a36Sopenharmony_ci struct fwnode_handle *ep; 65162306a36Sopenharmony_ci int ret; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0); 65462306a36Sopenharmony_ci if (!ep) { 65562306a36Sopenharmony_ci dev_err(dev, "not connected to subdevice\n"); 65662306a36Sopenharmony_ci return -EINVAL; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep); 66062306a36Sopenharmony_ci if (ret) { 66162306a36Sopenharmony_ci dev_err(dev, "could not parse v4l2 endpoint\n"); 66262306a36Sopenharmony_ci fwnode_handle_put(ep); 66362306a36Sopenharmony_ci return -EINVAL; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci fwnode = fwnode_graph_get_remote_endpoint(ep); 66762306a36Sopenharmony_ci fwnode_handle_put(ep); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci v4l2_async_subdev_nf_init(&csi->notifier, &csi->subdev); 67062306a36Sopenharmony_ci csi->notifier.ops = &mei_csi_notify_ops; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci asd = v4l2_async_nf_add_fwnode(&csi->notifier, fwnode, 67362306a36Sopenharmony_ci struct v4l2_async_connection); 67462306a36Sopenharmony_ci if (IS_ERR(asd)) { 67562306a36Sopenharmony_ci fwnode_handle_put(fwnode); 67662306a36Sopenharmony_ci return PTR_ERR(asd); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci ret = v4l2_fwnode_endpoint_alloc_parse(fwnode, &v4l2_ep); 68062306a36Sopenharmony_ci fwnode_handle_put(fwnode); 68162306a36Sopenharmony_ci if (ret) 68262306a36Sopenharmony_ci return ret; 68362306a36Sopenharmony_ci csi->nr_of_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ret = v4l2_async_nf_register(&csi->notifier); 68662306a36Sopenharmony_ci if (ret) 68762306a36Sopenharmony_ci v4l2_async_nf_cleanup(&csi->notifier); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci v4l2_fwnode_endpoint_free(&v4l2_ep); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return ret; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int mei_csi_probe(struct mei_cl_device *cldev, 69562306a36Sopenharmony_ci const struct mei_cl_device_id *id) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct device *dev = &cldev->dev; 69862306a36Sopenharmony_ci struct mei_csi *csi; 69962306a36Sopenharmony_ci int ret; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (!dev_fwnode(dev)) 70262306a36Sopenharmony_ci return -EPROBE_DEFER; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci csi = devm_kzalloc(dev, sizeof(struct mei_csi), GFP_KERNEL); 70562306a36Sopenharmony_ci if (!csi) 70662306a36Sopenharmony_ci return -ENOMEM; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci csi->cldev = cldev; 70962306a36Sopenharmony_ci mutex_init(&csi->lock); 71062306a36Sopenharmony_ci init_completion(&csi->cmd_completion); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci mei_cldev_set_drvdata(cldev, csi); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci ret = mei_cldev_enable(cldev); 71562306a36Sopenharmony_ci if (ret < 0) { 71662306a36Sopenharmony_ci dev_err(dev, "mei_cldev_enable failed: %d\n", ret); 71762306a36Sopenharmony_ci goto destroy_mutex; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci ret = mei_cldev_register_rx_cb(cldev, mei_csi_rx); 72162306a36Sopenharmony_ci if (ret) { 72262306a36Sopenharmony_ci dev_err(dev, "event cb registration failed: %d\n", ret); 72362306a36Sopenharmony_ci goto err_disable; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ret = mei_csi_parse_firmware(csi); 72762306a36Sopenharmony_ci if (ret) 72862306a36Sopenharmony_ci goto err_disable; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci csi->subdev.dev = &cldev->dev; 73162306a36Sopenharmony_ci v4l2_subdev_init(&csi->subdev, &mei_csi_subdev_ops); 73262306a36Sopenharmony_ci v4l2_set_subdevdata(&csi->subdev, csi); 73362306a36Sopenharmony_ci csi->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | 73462306a36Sopenharmony_ci V4L2_SUBDEV_FL_HAS_EVENTS; 73562306a36Sopenharmony_ci csi->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 73662306a36Sopenharmony_ci csi->subdev.entity.ops = &mei_csi_entity_ops; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci snprintf(csi->subdev.name, sizeof(csi->subdev.name), 73962306a36Sopenharmony_ci MEI_CSI_ENTITY_NAME); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci ret = mei_csi_init_controls(csi); 74262306a36Sopenharmony_ci if (ret) 74362306a36Sopenharmony_ci goto err_ctrl_handler; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci csi->format_mbus[CSI_PAD_SOURCE] = mei_csi_format_mbus_default; 74662306a36Sopenharmony_ci csi->format_mbus[CSI_PAD_SINK] = mei_csi_format_mbus_default; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci csi->pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 74962306a36Sopenharmony_ci csi->pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 75062306a36Sopenharmony_ci ret = media_entity_pads_init(&csi->subdev.entity, CSI_NUM_PADS, 75162306a36Sopenharmony_ci csi->pads); 75262306a36Sopenharmony_ci if (ret) 75362306a36Sopenharmony_ci goto err_ctrl_handler; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ret = v4l2_subdev_init_finalize(&csi->subdev); 75662306a36Sopenharmony_ci if (ret < 0) 75762306a36Sopenharmony_ci goto err_entity; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ret = v4l2_async_register_subdev(&csi->subdev); 76062306a36Sopenharmony_ci if (ret < 0) 76162306a36Sopenharmony_ci goto err_subdev; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci pm_runtime_enable(&cldev->dev); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cierr_subdev: 76862306a36Sopenharmony_ci v4l2_subdev_cleanup(&csi->subdev); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cierr_entity: 77162306a36Sopenharmony_ci media_entity_cleanup(&csi->subdev.entity); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cierr_ctrl_handler: 77462306a36Sopenharmony_ci v4l2_ctrl_handler_free(&csi->ctrl_handler); 77562306a36Sopenharmony_ci v4l2_async_nf_unregister(&csi->notifier); 77662306a36Sopenharmony_ci v4l2_async_nf_cleanup(&csi->notifier); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cierr_disable: 77962306a36Sopenharmony_ci mei_cldev_disable(cldev); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cidestroy_mutex: 78262306a36Sopenharmony_ci mutex_destroy(&csi->lock); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return ret; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic void mei_csi_remove(struct mei_cl_device *cldev) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci struct mei_csi *csi = mei_cldev_get_drvdata(cldev); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci v4l2_async_nf_unregister(&csi->notifier); 79262306a36Sopenharmony_ci v4l2_async_nf_cleanup(&csi->notifier); 79362306a36Sopenharmony_ci v4l2_ctrl_handler_free(&csi->ctrl_handler); 79462306a36Sopenharmony_ci v4l2_async_unregister_subdev(&csi->subdev); 79562306a36Sopenharmony_ci v4l2_subdev_cleanup(&csi->subdev); 79662306a36Sopenharmony_ci media_entity_cleanup(&csi->subdev.entity); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci pm_runtime_disable(&cldev->dev); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci mutex_destroy(&csi->lock); 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci#define MEI_CSI_UUID UUID_LE(0x92335FCF, 0x3203, 0x4472, \ 80462306a36Sopenharmony_ci 0xAF, 0x93, 0x7b, 0x44, 0x53, 0xAC, 0x29, 0xDA) 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic const struct mei_cl_device_id mei_csi_tbl[] = { 80762306a36Sopenharmony_ci { MEI_CSI_DRIVER_NAME, MEI_CSI_UUID, MEI_CL_VERSION_ANY }, 80862306a36Sopenharmony_ci { /* sentinel */ } 80962306a36Sopenharmony_ci}; 81062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(mei, mei_csi_tbl); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic struct mei_cl_driver mei_csi_driver = { 81362306a36Sopenharmony_ci .id_table = mei_csi_tbl, 81462306a36Sopenharmony_ci .name = MEI_CSI_DRIVER_NAME, 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci .probe = mei_csi_probe, 81762306a36Sopenharmony_ci .remove = mei_csi_remove, 81862306a36Sopenharmony_ci}; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cimodule_mei_cl_driver(mei_csi_driver); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ciMODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>"); 82362306a36Sopenharmony_ciMODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>"); 82462306a36Sopenharmony_ciMODULE_DESCRIPTION("Device driver for IVSC CSI"); 82562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 826