From 04dd17822334871b23ea2862f7798fb0e0007777 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Sat, 11 May 2024 08:53:19 +0000 Subject: [PATCH] change otg to host mode --- kernel/drivers/media/i2c/lt6911uxc.c | 445 ++++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 339 insertions(+), 106 deletions(-) diff --git a/kernel/drivers/media/i2c/lt6911uxc.c b/kernel/drivers/media/i2c/lt6911uxc.c index 88ce0ca..dc30a14 100644 --- a/kernel/drivers/media/i2c/lt6911uxc.c +++ b/kernel/drivers/media/i2c/lt6911uxc.c @@ -3,6 +3,11 @@ * Copyright (c) 2021 Rockchip Electronics Co. Ltd. * * Author: Dingxian Wen <shawn.wen@rock-chips.com> + * V0.0X01.0X00 first version. + * V0.0X01.0X01 fix if plugin_gpio was not used. + * V0.0X01.0X02 modify driver init level to late_initcall. + * V0.0X01.0X03 add 4K60 dual mipi support + * */ #include <linux/clk.h> @@ -15,12 +20,14 @@ #include <linux/module.h> #include <linux/of_graph.h> #include <linux/rk-camera-module.h> +#include <linux/rk_hdmirx_class.h> #include <linux/slab.h> #include <linux/timer.h> #include <linux/v4l2-dv-timings.h> #include <linux/version.h> #include <linux/videodev2.h> #include <linux/workqueue.h> +#include <linux/compat.h> #include <media/v4l2-controls_rockchip.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -29,22 +36,36 @@ #include <media/v4l2-fwnode.h> #include "lt6911uxc.h" -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x3) #define LT6911UXC_NAME "LT6911UXC" -#define LT6911UXC_LINK_FREQ_HIGH 400000000 -#define LT6911UXC_LINK_FREQ_LOW 200000000 -#define LT6911UXC_PIXEL_RATE 400000000 +#define LT6911UXC_LINK_FREQ_650M 650000000 +#define LT6911UXC_LINK_FREQ_400M 400000000 +#define LT6911UXC_LINK_FREQ_300M 300000000 +#define LT6911UXC_LINK_FREQ_200M 200000000 +#define LT6911UXC_LINK_FREQ_100M 100000000 +#define LT6911UXC_LINK_FREQ_60M 60000000 +#define LT6911UXC_PIXEL_RATE 600000000 #define I2C_MAX_XFER_SIZE 128 + +#ifdef LT6911UXC_OUT_RGB +#define LT6911UXC_MEDIA_BUS_FMT MEDIA_BUS_FMT_BGR888_1X24 +#else +#define LT6911UXC_MEDIA_BUS_FMT MEDIA_BUS_FMT_UYVY8_2X8 +#endif static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); static const s64 link_freq_menu_items[] = { - LT6911UXC_LINK_FREQ_HIGH, - LT6911UXC_LINK_FREQ_LOW, + LT6911UXC_LINK_FREQ_650M, + LT6911UXC_LINK_FREQ_400M, + LT6911UXC_LINK_FREQ_300M, + LT6911UXC_LINK_FREQ_200M, + LT6911UXC_LINK_FREQ_100M, + LT6911UXC_LINK_FREQ_60M, }; struct lt6911uxc { @@ -67,6 +88,7 @@ struct v4l2_dv_timings timings; struct v4l2_fwnode_bus_mipi_csi2 bus; struct v4l2_subdev sd; + struct rkmodule_multi_dev_info multi_dev_info; const char *len_name; const char *module_facing; const char *module_name; @@ -79,6 +101,7 @@ u32 module_index; u32 csi_lanes_in_use; u32 audio_sampling_rate; + struct device *classdev; }; struct lt6911uxc_mode { @@ -88,13 +111,25 @@ u32 hts_def; u32 vts_def; u32 exp_def; + u32 mipi_freq_idx; +}; + +static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { + .vendor = PHY_VENDOR_SAMSUNG, + .lp_vol_ref = 3, + .lp_hys_sw = {3, 0, 3, 0}, + .lp_escclk_pol_sel = {1, 1, 0, 0}, + .skew_data_cal_clk = {0, 0, 0, 0}, + .clk_hs_term_sel = 2, + .data_hs_term_sel = {2, 2, 2, 2}, + .reserved = {0}, }; static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 400000000, + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 600000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | @@ -109,10 +144,21 @@ .height = 2160, .max_fps = { .numerator = 10000, + .denominator = 600000, + }, + .hts_def = 4400, + .vts_def = 2250, + .mipi_freq_idx = 0, + }, { + .width = 3840, + .height = 2160, + .max_fps = { + .numerator = 10000, .denominator = 300000, }, .hts_def = 4400, .vts_def = 2250, + .mipi_freq_idx = 0, }, { .width = 1920, .height = 1080, @@ -122,6 +168,7 @@ }, .hts_def = 2200, .vts_def = 1125, + .mipi_freq_idx = 2, }, { .width = 1920, .height = 540, @@ -129,6 +176,7 @@ .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 3, }, { .width = 1440, .height = 240, @@ -136,6 +184,7 @@ .numerator = 10000, .denominator = 600000, }, + .mipi_freq_idx = 4, }, { .width = 1440, .height = 288, @@ -143,6 +192,7 @@ .numerator = 10000, .denominator = 500000, }, + .mipi_freq_idx = 4, }, { .width = 1280, .height = 720, @@ -152,6 +202,7 @@ }, .hts_def = 1650, .vts_def = 750, + .mipi_freq_idx = 3, }, { .width = 720, .height = 576, @@ -161,6 +212,7 @@ }, .hts_def = 864, .vts_def = 625, + .mipi_freq_idx = 5, }, { .width = 720, .height = 480, @@ -170,6 +222,7 @@ }, .hts_def = 858, .vts_def = 525, + .mipi_freq_idx = 5, }, }; @@ -286,14 +339,27 @@ static inline bool tx_5v_power_present(struct v4l2_subdev *sd) { - int val; + bool ret; + int val, i, cnt; struct lt6911uxc *lt6911uxc = to_state(sd); - val = gpiod_get_value(lt6911uxc->plugin_det_gpio); - v4l2_dbg(1, debug, sd, "%s plug det: %s!\n", __func__, - (val > 0) ? "int" : "out"); + /* if not use plugin det gpio */ + if (!lt6911uxc->plugin_det_gpio) + return true; - return (val > 0); + cnt = 0; + for (i = 0; i < 5; i++) { + val = gpiod_get_value(lt6911uxc->plugin_det_gpio); + + if (val > 0) + cnt++; + usleep_range(500, 600); + } + + ret = (cnt >= 3) ? true : false; + v4l2_dbg(1, debug, sd, "%s: %d\n", __func__, ret); + + return ret; } static inline bool no_signal(struct v4l2_subdev *sd) @@ -365,6 +431,7 @@ u8 value, val_h, val_l; u32 fw_ver, mipi_byte_clk, mipi_bitrate; u8 fw_a, fw_b, fw_c, fw_d, lanes; + u8 video_fmt; int ret; memset(timings, 0, sizeof(struct v4l2_dv_timings)); @@ -396,13 +463,15 @@ i2c_rd8(sd, MIPI_LANES, &lanes); lt6911uxc->csi_lanes_in_use = lanes; + if (lt6911uxc->csi_lanes_in_use == 8) + v4l2_info(sd, "get 8 lane in use, set dual mipi mode\n"); i2c_wr8(sd, FM1_DET_CLK_SRC_SEL, AD_LMTX_WRITE_CLK); i2c_rd8(sd, FREQ_METER_H, &clk_h); i2c_rd8(sd, FREQ_METER_M, &clk_m); i2c_rd8(sd, FREQ_METER_L, &clk_l); mipi_byte_clk = (((clk_h & 0xf) << 16) | (clk_m << 8) | clk_l); mipi_bitrate = mipi_byte_clk * 8 / 1000; - v4l2_info(sd, "MIPI Byte clk: %dKHz, MIPI bitrate: %dMbps, lanes:%d\n", + v4l2_info(sd, "MIPI Byte clk: %uKHz, MIPI bitrate: %uMbps, lanes:%d\n", mipi_byte_clk, mipi_bitrate, lanes); i2c_rd8(sd, HTOTAL_H, &val_h); @@ -432,7 +501,15 @@ hbp = ((val_h << 8) | val_l) * 2; i2c_rd8(sd, VBP, &value); vbp = value; + i2c_rd8(sd, COLOR_FMT_STATUS, &video_fmt); + video_fmt = (video_fmt & GENMASK(6, 5)) >> 5; lt6911uxc_i2c_disable(sd); + + if (video_fmt == 0x3) { + lt6911uxc->nosignal = true; + v4l2_err(sd, "%s ERROR: HDMI input YUV420, don't support YUV420!\n", __func__); + return -EINVAL; + } if (!lt6911uxc_rcv_supported_res(sd, hact, vact)) { lt6911uxc->nosignal = true; @@ -505,6 +582,8 @@ struct v4l2_subdev *sd = <6911uxc->sd; v4l2_dbg(2, debug, sd, "%s:\n", __func__); + + v4l2_ctrl_s_ctrl(lt6911uxc->detect_tx_5v_ctrl, tx_5v_power_present(sd)); lt6911uxc_config_hpd(sd); } @@ -589,21 +668,6 @@ v4l2_subdev_notify_event(sd, <6911uxc_ev_fmt); } -static int lt6911uxc_get_ctrl(struct v4l2_ctrl *ctrl) -{ - int ret = -1; - struct lt6911uxc *lt6911uxc = container_of(ctrl->handler, - struct lt6911uxc, hdl); - struct v4l2_subdev *sd = &(lt6911uxc->sd); - - if (ctrl->id == V4L2_CID_DV_RX_POWER_PRESENT) { - ret = tx_5v_power_present(sd); - *ctrl->p_new.p_s32 = ret; - } - - return ret; -} - static int lt6911uxc_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct lt6911uxc *lt6911uxc = to_state(sd); @@ -682,7 +746,6 @@ } lt6911uxc->timings = *timings; - enable_stream(sd, false); return 0; @@ -738,12 +801,12 @@ return 0; } -static int lt6911uxc_g_mbus_config(struct v4l2_subdev *sd, +static int lt6911uxc_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, struct v4l2_mbus_config *cfg) { struct lt6911uxc *lt6911uxc = to_state(sd); - cfg->type = V4L2_MBUS_CSI2; + cfg->type = V4L2_MBUS_CSI2_DPHY; cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_CHANNEL_0; switch (lt6911uxc->csi_lanes_in_use) { @@ -759,7 +822,9 @@ case 4: cfg->flags |= V4L2_MBUS_CSI2_4_LANE; break; - + case 8: + cfg->flags |= V4L2_MBUS_CSI2_4_LANE; + break; default: return -EINVAL; } @@ -780,7 +845,7 @@ { switch (code->index) { case 0: - code->code = MEDIA_BUS_FMT_UYVY8_2X8; + code->code = LT6911UXC_MEDIA_BUS_FMT; break; default: @@ -797,7 +862,7 @@ if (fse->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fse->code != MEDIA_BUS_FMT_UYVY8_2X8) + if (fse->code != LT6911UXC_MEDIA_BUS_FMT) return -EINVAL; fse->min_width = supported_modes[fse->index].width; @@ -815,8 +880,7 @@ if (fie->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fie->code != MEDIA_BUS_FMT_UYVY8_2X8) - return -EINVAL; + fie->code = LT6911UXC_MEDIA_BUS_FMT; fie->width = supported_modes[fie->index].width; fie->height = supported_modes[fie->index].height; @@ -825,11 +889,50 @@ return 0; } +static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, + struct v4l2_dv_timings *timings) +{ + struct v4l2_bt_timings *bt = &timings->bt; + u32 cur_fps, dist_fps; + + cur_fps = fps_calc(bt); + dist_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator); + + return abs(mode->width - bt->width) + + abs(mode->height - bt->height) + abs(dist_fps - cur_fps); +} + +static const struct lt6911uxc_mode * +lt6911uxc_find_best_fit(struct lt6911uxc *lt6911uxc) +{ + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + dist = lt6911uxc_get_reso_dist(&supported_modes[i], <6911uxc->timings); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; + } + } + dev_info(<6911uxc->i2c_client->dev, + "find current mode: support_mode[%d], %dx%d@%dfps\n", + cur_best_fit, supported_modes[cur_best_fit].width, + supported_modes[cur_best_fit].height, + DIV_ROUND_CLOSEST(supported_modes[cur_best_fit].max_fps.denominator, + supported_modes[cur_best_fit].max_fps.numerator)); + + return &supported_modes[cur_best_fit]; +} + static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); + const struct lt6911uxc_mode *mode; mutex_lock(<6911uxc->confctl_mutex); format->format.code = lt6911uxc->mbus_fmt_code; @@ -839,6 +942,14 @@ lt6911uxc->timings.bt.interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; format->format.colorspace = V4L2_COLORSPACE_SRGB; + + mode = lt6911uxc_find_best_fit(lt6911uxc); + lt6911uxc->cur_mode = mode; + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, + LT6911UXC_PIXEL_RATE); + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, + mode->mipi_freq_idx); + mutex_unlock(<6911uxc->confctl_mutex); v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field mode:%s\n", @@ -848,40 +959,12 @@ return 0; } -static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, - struct v4l2_mbus_framefmt *framefmt) -{ - return abs(mode->width - framefmt->width) + - abs(mode->height - framefmt->height); -} - -static const struct lt6911uxc_mode * -lt6911uxc_find_best_fit(struct v4l2_subdev_format *fmt) -{ - struct v4l2_mbus_framefmt *framefmt = &fmt->format; - int dist; - int cur_best_fit = 0; - int cur_best_fit_dist = -1; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { - dist = lt6911uxc_get_reso_dist(&supported_modes[i], framefmt); - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { - cur_best_fit_dist = dist; - cur_best_fit = i; - } - } - - return &supported_modes[cur_best_fit]; -} - static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct lt6911uxc *lt6911uxc = to_state(sd); const struct lt6911uxc_mode *mode; - int index; /* is overwritten by get_fmt */ u32 code = format->format.code; @@ -893,7 +976,7 @@ return ret; switch (code) { - case MEDIA_BUS_FMT_UYVY8_2X8: + case LT6911UXC_MEDIA_BUS_FMT: break; default: @@ -904,19 +987,9 @@ return 0; lt6911uxc->mbus_fmt_code = format->format.code; - mode = lt6911uxc_find_best_fit(format); + mode = lt6911uxc_find_best_fit(lt6911uxc); lt6911uxc->cur_mode = mode; enable_stream(sd, false); - - if (((mode->width == 720) && (mode->height == 576)) || - ((mode->width == 720) && (mode->height == 480))) - index = 1; - else - index = 0; - - __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, index); - v4l2_dbg(1, debug, sd, "%s res wxh:%dx%d, link freq:%llu", __func__, - mode->width, mode->height, link_freq_menu_items[index]); return 0; } @@ -946,11 +1019,40 @@ static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct lt6911uxc *lt6911uxc = to_state(sd); + struct device *dev = <6911uxc->i2c_client->dev; long ret = 0; + struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: lt6911uxc_get_module_inf(lt6911uxc, (struct rkmodule_inf *)arg); + break; + case RKMODULE_GET_HDMI_MODE: + *(int *)arg = RKMODULE_HDMIIN_MODE; + break; + case RKMODULE_SET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + if (dphy_param->vendor == PHY_VENDOR_SAMSUNG) + rk3588_dcphy_param = *dphy_param; + dev_dbg(<6911uxc->i2c_client->dev, + "sensor set dphy param\n"); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = (struct rkmodule_csi_dphy_param *)arg; + *dphy_param = rk3588_dcphy_param; + dev_dbg(<6911uxc->i2c_client->dev, + "sensor get dphy param\n"); + break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = (struct rkmodule_capture_info *)arg; + if (lt6911uxc->csi_lanes_in_use == 8) { + dev_info(dev, "8 lanes in use, set dual mipi mode\n"); + capture_info->mode = RKMODULE_MULTI_DEV_COMBINE_ONE; + capture_info->multi_dev = lt6911uxc->multi_dev_info; + } else { + capture_info->mode = 0; + } break; default: ret = -ENOIOCTLCMD; @@ -967,6 +1069,9 @@ void __user *up = compat_ptr(arg); struct rkmodule_inf *inf; long ret; + int *seq; + struct rkmodule_csi_dphy_param *dphy_param; + struct rkmodule_capture_info *capture_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -984,7 +1089,65 @@ } kfree(inf); break; + case RKMODULE_GET_HDMI_MODE: + seq = kzalloc(sizeof(*seq), GFP_KERNEL); + if (!seq) { + ret = -ENOMEM; + return ret; + } + ret = lt6911uxc_ioctl(sd, cmd, seq); + if (!ret) { + ret = copy_to_user(up, seq, sizeof(*seq)); + if (ret) + ret = -EFAULT; + } + kfree(seq); + break; + case RKMODULE_SET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(dphy_param, up, sizeof(*dphy_param)); + if (!ret) + ret = lt6911uxc_ioctl(sd, cmd, dphy_param); + else + ret = -EFAULT; + kfree(dphy_param); + break; + case RKMODULE_GET_CSI_DPHY_PARAM: + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); + if (!dphy_param) { + ret = -ENOMEM; + return ret; + } + + ret = lt6911uxc_ioctl(sd, cmd, dphy_param); + if (!ret) { + ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); + if (ret) + ret = -EFAULT; + } + kfree(dphy_param); + break; + case RKMODULE_GET_CAPTURE_MODE: + capture_info = kzalloc(sizeof(*capture_info), GFP_KERNEL); + if (!capture_info) { + ret = -ENOMEM; + return ret; + } + + ret = lt6911uxc_ioctl(sd, cmd, capture_info); + if (!ret) { + ret = copy_to_user(up, capture_info, sizeof(*capture_info)); + if (ret) + ret = -EFAULT; + } + kfree(capture_info); + break; default: ret = -ENOIOCTLCMD; break; @@ -993,10 +1156,6 @@ return ret; } #endif - -static const struct v4l2_ctrl_ops lt6911uxc_ctrl_ops = { - .g_volatile_ctrl = lt6911uxc_get_ctrl, -}; static const struct v4l2_subdev_core_ops lt6911uxc_core_ops = { .interrupt_service_routine = lt6911uxc_isr, @@ -1013,7 +1172,6 @@ .s_dv_timings = lt6911uxc_s_dv_timings, .g_dv_timings = lt6911uxc_g_dv_timings, .query_dv_timings = lt6911uxc_query_dv_timings, - .g_mbus_config = lt6911uxc_g_mbus_config, .s_stream = lt6911uxc_s_stream, .g_frame_interval = lt6911uxc_g_frame_interval, }; @@ -1026,6 +1184,7 @@ .get_fmt = lt6911uxc_get_fmt, .enum_dv_timings = lt6911uxc_enum_dv_timings, .dv_timings_cap = lt6911uxc_dv_timings_cap, + .get_mbus_config = lt6911uxc_g_mbus_config, }; static const struct v4l2_subdev_ops lt6911uxc_ops = { @@ -1068,9 +1227,11 @@ static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) { + const struct lt6911uxc_mode *mode; struct v4l2_subdev *sd; int ret; + mode = lt6911uxc->cur_mode; sd = <6911uxc->sd; ret = v4l2_ctrl_handler_init(<6911uxc->hdl, 5); if (ret) @@ -1080,14 +1241,13 @@ V4L2_CID_LINK_FREQ, ARRAY_SIZE(link_freq_menu_items) - 1, 0, link_freq_menu_items); - v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_PIXEL_RATE, - 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); + lt6911uxc->pixel_rate = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, + V4L2_CID_PIXEL_RATE, + 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); lt6911uxc->detect_tx_5v_ctrl = v4l2_ctrl_new_std(<6911uxc->hdl, - <6911uxc_ctrl_ops, V4L2_CID_DV_RX_POWER_PRESENT, + NULL, V4L2_CID_DV_RX_POWER_PRESENT, 0, 1, 0, 0); - if (lt6911uxc->detect_tx_5v_ctrl) - lt6911uxc->detect_tx_5v_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; lt6911uxc->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(<6911uxc->hdl, @@ -1101,6 +1261,9 @@ v4l2_err(sd, "cfg v4l2 ctrls failed! ret:%d\n", ret); return ret; } + + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, mode->mipi_freq_idx); + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, LT6911UXC_PIXEL_RATE); if (lt6911uxc_update_controls(sd)) { ret = -ENODEV; @@ -1153,7 +1316,7 @@ { struct device *dev = <6911uxc->i2c_client->dev; struct device_node *node = dev->of_node; - struct v4l2_fwnode_endpoint *endpoint; + struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; struct device_node *ep; int ret; @@ -1209,15 +1372,14 @@ return ret; } - endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep)); - if (IS_ERR(endpoint)) { + ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); + if (ret) { dev_err(dev, "failed to parse endpoint\n"); - ret = PTR_ERR(endpoint); - return ret; + goto put_node; } - if (endpoint->bus_type != V4L2_MBUS_CSI2 || - endpoint->bus.mipi_csi2.num_data_lanes == 0) { + if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || + endpoint.bus.mipi_csi2.num_data_lanes == 0) { dev_err(dev, "missing CSI-2 properties in endpoint\n"); ret = -EINVAL; goto free_endpoint; @@ -1236,8 +1398,8 @@ goto free_endpoint; } - lt6911uxc->csi_lanes_in_use = endpoint->bus.mipi_csi2.num_data_lanes; - lt6911uxc->bus = endpoint->bus.mipi_csi2; + lt6911uxc->csi_lanes_in_use = endpoint.bus.mipi_csi2.num_data_lanes; + lt6911uxc->bus = endpoint.bus.mipi_csi2; lt6911uxc->enable_hdcp = false; gpiod_set_value(lt6911uxc->hpd_ctl_gpio, 0); @@ -1247,7 +1409,9 @@ ret = 0; free_endpoint: - v4l2_fwnode_endpoint_free(endpoint); + v4l2_fwnode_endpoint_free(&endpoint); +put_node: + of_node_put(ep); return ret; } #else @@ -1257,9 +1421,69 @@ } #endif +static ssize_t audio_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lt6911uxc *lt6911uxc = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d", lt6911uxc->audio_sampling_rate); +} + +static ssize_t audio_present_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lt6911uxc *lt6911uxc = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%d", + tx_5v_power_present(<6911uxc->sd) ? + lt6911uxc->is_audio_present : 0); +} + +static DEVICE_ATTR_RO(audio_rate); +static DEVICE_ATTR_RO(audio_present); + +static struct attribute *lt6911_attrs[] = { + &dev_attr_audio_rate.attr, + &dev_attr_audio_present.attr, + NULL +}; +ATTRIBUTE_GROUPS(lt6911); + +static int lt6911uxc_get_multi_dev_info(struct lt6911uxc *lt6911uxc) +{ + struct device *dev = <6911uxc->i2c_client->dev; + struct device_node *node = dev->of_node; + struct device_node *multi_info_np; + + multi_info_np = of_get_child_by_name(node, "multi-dev-info"); + if (!multi_info_np) { + dev_info(dev, "failed to get multi dev info\n"); + return -EINVAL; + } + + of_property_read_u32(multi_info_np, "dev-idx-l", + <6911uxc->multi_dev_info.dev_idx[0]); + of_property_read_u32(multi_info_np, "dev-idx-r", + <6911uxc->multi_dev_info.dev_idx[1]); + of_property_read_u32(multi_info_np, "combine-idx", + <6911uxc->multi_dev_info.combine_idx[0]); + of_property_read_u32(multi_info_np, "pixel-offset", + <6911uxc->multi_dev_info.pixel_offset); + of_property_read_u32(multi_info_np, "dev-num", + <6911uxc->multi_dev_info.dev_num); + dev_info(dev, + "multi dev left: mipi%d, multi dev right: mipi%d, combile mipi%d, dev num: %d\n", + lt6911uxc->multi_dev_info.dev_idx[0], lt6911uxc->multi_dev_info.dev_idx[1], + lt6911uxc->multi_dev_info.combine_idx[0], lt6911uxc->multi_dev_info.dev_num); + + return 0; +} + static int lt6911uxc_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct v4l2_dv_timings default_timing = + V4L2_DV_BT_CEA_640X480P59_94; struct lt6911uxc *lt6911uxc; struct v4l2_subdev *sd; struct device *dev = &client->dev; @@ -1277,14 +1501,19 @@ sd = <6911uxc->sd; lt6911uxc->i2c_client = client; + lt6911uxc->timings = default_timing; lt6911uxc->cur_mode = &supported_modes[0]; - lt6911uxc->mbus_fmt_code = MEDIA_BUS_FMT_UYVY8_2X8; + lt6911uxc->mbus_fmt_code = LT6911UXC_MEDIA_BUS_FMT; err = lt6911uxc_parse_of(lt6911uxc); if (err) { v4l2_err(sd, "lt6911uxc_parse_of failed! err:%d\n", err); return err; } + + err = lt6911uxc_get_multi_dev_info(lt6911uxc); + if (err) + v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); err = lt6911uxc_check_chip_id(lt6911uxc); if (err < 0) @@ -1330,6 +1559,14 @@ goto err_clean_entity; } + lt6911uxc->classdev = device_create_with_groups(rk_hdmirx_class(), + dev, MKDEV(0, 0), + lt6911uxc, + lt6911_groups, + "lt6911"); + if (IS_ERR(lt6911uxc->classdev)) + goto err_clean_entity; + INIT_DELAYED_WORK(<6911uxc->delayed_work_enable_hotplug, lt6911uxc_delayed_work_enable_hotplug); INIT_DELAYED_WORK(<6911uxc->delayed_work_res_change, @@ -1353,20 +1590,15 @@ } lt6911uxc->plugin_irq = gpiod_to_irq(lt6911uxc->plugin_det_gpio); - if (lt6911uxc->plugin_irq < 0) { - dev_err(dev, "failed to get plugin det irq\n"); - err = lt6911uxc->plugin_irq; - goto err_work_queues; - } + if (lt6911uxc->plugin_irq < 0) + dev_err(dev, "failed to get plugin det irq, maybe no use\n"); err = devm_request_threaded_irq(dev, lt6911uxc->plugin_irq, NULL, plugin_detect_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "lt6911uxc", lt6911uxc); - if (err) { - dev_err(dev, "failed to register plugin det irq (%d)\n", err); - goto err_work_queues; - } + if (err) + dev_err(dev, "failed to register plugin det irq (%d), maybe no use\n", err); err = v4l2_ctrl_handler_setup(sd->ctrl_handler); if (err) { @@ -1444,4 +1676,5 @@ MODULE_DESCRIPTION("Lontium LT6911UXC HDMI to MIPI CSI-2 bridge driver"); MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>"); +MODULE_AUTHOR("Jianwei Fan <jianwei.fan@rock-chips.com>"); MODULE_LICENSE("GPL v2"); -- Gitblit v1.6.2