| .. | .. |
|---|
| 6 | 6 | * V0.0X01.0X00 first version. |
|---|
| 7 | 7 | * V0.0X01.0X01 fix if plugin_gpio was not used. |
|---|
| 8 | 8 | * V0.0X01.0X02 modify driver init level to late_initcall. |
|---|
| 9 | + * V0.0X01.0X03 add 4K60 dual mipi support |
|---|
| 10 | + * |
|---|
| 9 | 11 | */ |
|---|
| 10 | 12 | |
|---|
| 11 | 13 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 18 | 20 | #include <linux/module.h> |
|---|
| 19 | 21 | #include <linux/of_graph.h> |
|---|
| 20 | 22 | #include <linux/rk-camera-module.h> |
|---|
| 23 | +#include <linux/rk_hdmirx_class.h> |
|---|
| 21 | 24 | #include <linux/slab.h> |
|---|
| 22 | 25 | #include <linux/timer.h> |
|---|
| 23 | 26 | #include <linux/v4l2-dv-timings.h> |
|---|
| .. | .. |
|---|
| 33 | 36 | #include <media/v4l2-fwnode.h> |
|---|
| 34 | 37 | #include "lt6911uxc.h" |
|---|
| 35 | 38 | |
|---|
| 36 | | -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) |
|---|
| 39 | +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x3) |
|---|
| 37 | 40 | #define LT6911UXC_NAME "LT6911UXC" |
|---|
| 38 | 41 | |
|---|
| 39 | | -#define LT6911UXC_LINK_FREQ_HIGH 400000000 |
|---|
| 40 | | -#define LT6911UXC_LINK_FREQ_LOW 200000000 |
|---|
| 41 | | -#define LT6911UXC_PIXEL_RATE 400000000 |
|---|
| 42 | +#define LT6911UXC_LINK_FREQ_650M 650000000 |
|---|
| 43 | +#define LT6911UXC_LINK_FREQ_400M 400000000 |
|---|
| 44 | +#define LT6911UXC_LINK_FREQ_300M 300000000 |
|---|
| 45 | +#define LT6911UXC_LINK_FREQ_200M 200000000 |
|---|
| 46 | +#define LT6911UXC_LINK_FREQ_100M 100000000 |
|---|
| 47 | +#define LT6911UXC_LINK_FREQ_60M 60000000 |
|---|
| 48 | +#define LT6911UXC_PIXEL_RATE 600000000 |
|---|
| 42 | 49 | |
|---|
| 43 | 50 | #define I2C_MAX_XFER_SIZE 128 |
|---|
| 44 | 51 | |
|---|
| .. | .. |
|---|
| 53 | 60 | MODULE_PARM_DESC(debug, "debug level (0-2)"); |
|---|
| 54 | 61 | |
|---|
| 55 | 62 | static const s64 link_freq_menu_items[] = { |
|---|
| 56 | | - LT6911UXC_LINK_FREQ_HIGH, |
|---|
| 57 | | - LT6911UXC_LINK_FREQ_LOW, |
|---|
| 63 | + LT6911UXC_LINK_FREQ_650M, |
|---|
| 64 | + LT6911UXC_LINK_FREQ_400M, |
|---|
| 65 | + LT6911UXC_LINK_FREQ_300M, |
|---|
| 66 | + LT6911UXC_LINK_FREQ_200M, |
|---|
| 67 | + LT6911UXC_LINK_FREQ_100M, |
|---|
| 68 | + LT6911UXC_LINK_FREQ_60M, |
|---|
| 58 | 69 | }; |
|---|
| 59 | 70 | |
|---|
| 60 | 71 | struct lt6911uxc { |
|---|
| .. | .. |
|---|
| 77 | 88 | struct v4l2_dv_timings timings; |
|---|
| 78 | 89 | struct v4l2_fwnode_bus_mipi_csi2 bus; |
|---|
| 79 | 90 | struct v4l2_subdev sd; |
|---|
| 91 | + struct rkmodule_multi_dev_info multi_dev_info; |
|---|
| 80 | 92 | const char *len_name; |
|---|
| 81 | 93 | const char *module_facing; |
|---|
| 82 | 94 | const char *module_name; |
|---|
| .. | .. |
|---|
| 89 | 101 | u32 module_index; |
|---|
| 90 | 102 | u32 csi_lanes_in_use; |
|---|
| 91 | 103 | u32 audio_sampling_rate; |
|---|
| 104 | + struct device *classdev; |
|---|
| 92 | 105 | }; |
|---|
| 93 | 106 | |
|---|
| 94 | 107 | struct lt6911uxc_mode { |
|---|
| .. | .. |
|---|
| 98 | 111 | u32 hts_def; |
|---|
| 99 | 112 | u32 vts_def; |
|---|
| 100 | 113 | u32 exp_def; |
|---|
| 114 | + u32 mipi_freq_idx; |
|---|
| 115 | +}; |
|---|
| 116 | + |
|---|
| 117 | +static struct rkmodule_csi_dphy_param rk3588_dcphy_param = { |
|---|
| 118 | + .vendor = PHY_VENDOR_SAMSUNG, |
|---|
| 119 | + .lp_vol_ref = 3, |
|---|
| 120 | + .lp_hys_sw = {3, 0, 3, 0}, |
|---|
| 121 | + .lp_escclk_pol_sel = {1, 1, 0, 0}, |
|---|
| 122 | + .skew_data_cal_clk = {0, 0, 0, 0}, |
|---|
| 123 | + .clk_hs_term_sel = 2, |
|---|
| 124 | + .data_hs_term_sel = {2, 2, 2, 2}, |
|---|
| 125 | + .reserved = {0}, |
|---|
| 101 | 126 | }; |
|---|
| 102 | 127 | |
|---|
| 103 | 128 | static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = { |
|---|
| 104 | 129 | .type = V4L2_DV_BT_656_1120, |
|---|
| 105 | 130 | /* keep this initialization for compatibility with GCC < 4.4.6 */ |
|---|
| 106 | 131 | .reserved = { 0 }, |
|---|
| 107 | | - V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 400000000, |
|---|
| 132 | + V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 600000000, |
|---|
| 108 | 133 | V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | |
|---|
| 109 | 134 | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, |
|---|
| 110 | 135 | V4L2_DV_BT_CAP_PROGRESSIVE | |
|---|
| .. | .. |
|---|
| 119 | 144 | .height = 2160, |
|---|
| 120 | 145 | .max_fps = { |
|---|
| 121 | 146 | .numerator = 10000, |
|---|
| 147 | + .denominator = 600000, |
|---|
| 148 | + }, |
|---|
| 149 | + .hts_def = 4400, |
|---|
| 150 | + .vts_def = 2250, |
|---|
| 151 | + .mipi_freq_idx = 0, |
|---|
| 152 | + }, { |
|---|
| 153 | + .width = 3840, |
|---|
| 154 | + .height = 2160, |
|---|
| 155 | + .max_fps = { |
|---|
| 156 | + .numerator = 10000, |
|---|
| 122 | 157 | .denominator = 300000, |
|---|
| 123 | 158 | }, |
|---|
| 124 | 159 | .hts_def = 4400, |
|---|
| 125 | 160 | .vts_def = 2250, |
|---|
| 161 | + .mipi_freq_idx = 0, |
|---|
| 126 | 162 | }, { |
|---|
| 127 | 163 | .width = 1920, |
|---|
| 128 | 164 | .height = 1080, |
|---|
| .. | .. |
|---|
| 132 | 168 | }, |
|---|
| 133 | 169 | .hts_def = 2200, |
|---|
| 134 | 170 | .vts_def = 1125, |
|---|
| 171 | + .mipi_freq_idx = 2, |
|---|
| 135 | 172 | }, { |
|---|
| 136 | 173 | .width = 1920, |
|---|
| 137 | 174 | .height = 540, |
|---|
| .. | .. |
|---|
| 139 | 176 | .numerator = 10000, |
|---|
| 140 | 177 | .denominator = 600000, |
|---|
| 141 | 178 | }, |
|---|
| 179 | + .mipi_freq_idx = 3, |
|---|
| 142 | 180 | }, { |
|---|
| 143 | 181 | .width = 1440, |
|---|
| 144 | 182 | .height = 240, |
|---|
| .. | .. |
|---|
| 146 | 184 | .numerator = 10000, |
|---|
| 147 | 185 | .denominator = 600000, |
|---|
| 148 | 186 | }, |
|---|
| 187 | + .mipi_freq_idx = 4, |
|---|
| 149 | 188 | }, { |
|---|
| 150 | 189 | .width = 1440, |
|---|
| 151 | 190 | .height = 288, |
|---|
| .. | .. |
|---|
| 153 | 192 | .numerator = 10000, |
|---|
| 154 | 193 | .denominator = 500000, |
|---|
| 155 | 194 | }, |
|---|
| 195 | + .mipi_freq_idx = 4, |
|---|
| 156 | 196 | }, { |
|---|
| 157 | 197 | .width = 1280, |
|---|
| 158 | 198 | .height = 720, |
|---|
| .. | .. |
|---|
| 162 | 202 | }, |
|---|
| 163 | 203 | .hts_def = 1650, |
|---|
| 164 | 204 | .vts_def = 750, |
|---|
| 205 | + .mipi_freq_idx = 3, |
|---|
| 165 | 206 | }, { |
|---|
| 166 | 207 | .width = 720, |
|---|
| 167 | 208 | .height = 576, |
|---|
| .. | .. |
|---|
| 171 | 212 | }, |
|---|
| 172 | 213 | .hts_def = 864, |
|---|
| 173 | 214 | .vts_def = 625, |
|---|
| 215 | + .mipi_freq_idx = 5, |
|---|
| 174 | 216 | }, { |
|---|
| 175 | 217 | .width = 720, |
|---|
| 176 | 218 | .height = 480, |
|---|
| .. | .. |
|---|
| 180 | 222 | }, |
|---|
| 181 | 223 | .hts_def = 858, |
|---|
| 182 | 224 | .vts_def = 525, |
|---|
| 225 | + .mipi_freq_idx = 5, |
|---|
| 183 | 226 | }, |
|---|
| 184 | 227 | }; |
|---|
| 185 | 228 | |
|---|
| .. | .. |
|---|
| 388 | 431 | u8 value, val_h, val_l; |
|---|
| 389 | 432 | u32 fw_ver, mipi_byte_clk, mipi_bitrate; |
|---|
| 390 | 433 | u8 fw_a, fw_b, fw_c, fw_d, lanes; |
|---|
| 434 | + u8 video_fmt; |
|---|
| 391 | 435 | int ret; |
|---|
| 392 | 436 | |
|---|
| 393 | 437 | memset(timings, 0, sizeof(struct v4l2_dv_timings)); |
|---|
| .. | .. |
|---|
| 419 | 463 | |
|---|
| 420 | 464 | i2c_rd8(sd, MIPI_LANES, &lanes); |
|---|
| 421 | 465 | lt6911uxc->csi_lanes_in_use = lanes; |
|---|
| 466 | + if (lt6911uxc->csi_lanes_in_use == 8) |
|---|
| 467 | + v4l2_info(sd, "get 8 lane in use, set dual mipi mode\n"); |
|---|
| 422 | 468 | i2c_wr8(sd, FM1_DET_CLK_SRC_SEL, AD_LMTX_WRITE_CLK); |
|---|
| 423 | 469 | i2c_rd8(sd, FREQ_METER_H, &clk_h); |
|---|
| 424 | 470 | i2c_rd8(sd, FREQ_METER_M, &clk_m); |
|---|
| 425 | 471 | i2c_rd8(sd, FREQ_METER_L, &clk_l); |
|---|
| 426 | 472 | mipi_byte_clk = (((clk_h & 0xf) << 16) | (clk_m << 8) | clk_l); |
|---|
| 427 | 473 | mipi_bitrate = mipi_byte_clk * 8 / 1000; |
|---|
| 428 | | - v4l2_info(sd, "MIPI Byte clk: %dKHz, MIPI bitrate: %dMbps, lanes:%d\n", |
|---|
| 474 | + v4l2_info(sd, "MIPI Byte clk: %uKHz, MIPI bitrate: %uMbps, lanes:%d\n", |
|---|
| 429 | 475 | mipi_byte_clk, mipi_bitrate, lanes); |
|---|
| 430 | 476 | |
|---|
| 431 | 477 | i2c_rd8(sd, HTOTAL_H, &val_h); |
|---|
| .. | .. |
|---|
| 455 | 501 | hbp = ((val_h << 8) | val_l) * 2; |
|---|
| 456 | 502 | i2c_rd8(sd, VBP, &value); |
|---|
| 457 | 503 | vbp = value; |
|---|
| 504 | + i2c_rd8(sd, COLOR_FMT_STATUS, &video_fmt); |
|---|
| 505 | + video_fmt = (video_fmt & GENMASK(6, 5)) >> 5; |
|---|
| 458 | 506 | lt6911uxc_i2c_disable(sd); |
|---|
| 507 | + |
|---|
| 508 | + if (video_fmt == 0x3) { |
|---|
| 509 | + lt6911uxc->nosignal = true; |
|---|
| 510 | + v4l2_err(sd, "%s ERROR: HDMI input YUV420, don't support YUV420!\n", __func__); |
|---|
| 511 | + return -EINVAL; |
|---|
| 512 | + } |
|---|
| 459 | 513 | |
|---|
| 460 | 514 | if (!lt6911uxc_rcv_supported_res(sd, hact, vact)) { |
|---|
| 461 | 515 | lt6911uxc->nosignal = true; |
|---|
| .. | .. |
|---|
| 692 | 746 | } |
|---|
| 693 | 747 | |
|---|
| 694 | 748 | lt6911uxc->timings = *timings; |
|---|
| 695 | | - |
|---|
| 696 | 749 | enable_stream(sd, false); |
|---|
| 697 | 750 | |
|---|
| 698 | 751 | return 0; |
|---|
| .. | .. |
|---|
| 769 | 822 | case 4: |
|---|
| 770 | 823 | cfg->flags |= V4L2_MBUS_CSI2_4_LANE; |
|---|
| 771 | 824 | break; |
|---|
| 772 | | - |
|---|
| 825 | + case 8: |
|---|
| 826 | + cfg->flags |= V4L2_MBUS_CSI2_4_LANE; |
|---|
| 827 | + break; |
|---|
| 773 | 828 | default: |
|---|
| 774 | 829 | return -EINVAL; |
|---|
| 775 | 830 | } |
|---|
| .. | .. |
|---|
| 834 | 889 | return 0; |
|---|
| 835 | 890 | } |
|---|
| 836 | 891 | |
|---|
| 892 | +static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, |
|---|
| 893 | + struct v4l2_dv_timings *timings) |
|---|
| 894 | +{ |
|---|
| 895 | + struct v4l2_bt_timings *bt = &timings->bt; |
|---|
| 896 | + u32 cur_fps, dist_fps; |
|---|
| 897 | + |
|---|
| 898 | + cur_fps = fps_calc(bt); |
|---|
| 899 | + dist_fps = DIV_ROUND_CLOSEST(mode->max_fps.denominator, mode->max_fps.numerator); |
|---|
| 900 | + |
|---|
| 901 | + return abs(mode->width - bt->width) + |
|---|
| 902 | + abs(mode->height - bt->height) + abs(dist_fps - cur_fps); |
|---|
| 903 | +} |
|---|
| 904 | + |
|---|
| 905 | +static const struct lt6911uxc_mode * |
|---|
| 906 | +lt6911uxc_find_best_fit(struct lt6911uxc *lt6911uxc) |
|---|
| 907 | +{ |
|---|
| 908 | + int dist; |
|---|
| 909 | + int cur_best_fit = 0; |
|---|
| 910 | + int cur_best_fit_dist = -1; |
|---|
| 911 | + unsigned int i; |
|---|
| 912 | + |
|---|
| 913 | + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { |
|---|
| 914 | + dist = lt6911uxc_get_reso_dist(&supported_modes[i], <6911uxc->timings); |
|---|
| 915 | + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { |
|---|
| 916 | + cur_best_fit_dist = dist; |
|---|
| 917 | + cur_best_fit = i; |
|---|
| 918 | + } |
|---|
| 919 | + } |
|---|
| 920 | + dev_info(<6911uxc->i2c_client->dev, |
|---|
| 921 | + "find current mode: support_mode[%d], %dx%d@%dfps\n", |
|---|
| 922 | + cur_best_fit, supported_modes[cur_best_fit].width, |
|---|
| 923 | + supported_modes[cur_best_fit].height, |
|---|
| 924 | + DIV_ROUND_CLOSEST(supported_modes[cur_best_fit].max_fps.denominator, |
|---|
| 925 | + supported_modes[cur_best_fit].max_fps.numerator)); |
|---|
| 926 | + |
|---|
| 927 | + return &supported_modes[cur_best_fit]; |
|---|
| 928 | +} |
|---|
| 929 | + |
|---|
| 837 | 930 | static int lt6911uxc_get_fmt(struct v4l2_subdev *sd, |
|---|
| 838 | 931 | struct v4l2_subdev_pad_config *cfg, |
|---|
| 839 | 932 | struct v4l2_subdev_format *format) |
|---|
| 840 | 933 | { |
|---|
| 841 | 934 | struct lt6911uxc *lt6911uxc = to_state(sd); |
|---|
| 935 | + const struct lt6911uxc_mode *mode; |
|---|
| 842 | 936 | |
|---|
| 843 | 937 | mutex_lock(<6911uxc->confctl_mutex); |
|---|
| 844 | 938 | format->format.code = lt6911uxc->mbus_fmt_code; |
|---|
| .. | .. |
|---|
| 848 | 942 | lt6911uxc->timings.bt.interlaced ? |
|---|
| 849 | 943 | V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE; |
|---|
| 850 | 944 | format->format.colorspace = V4L2_COLORSPACE_SRGB; |
|---|
| 945 | + |
|---|
| 946 | + mode = lt6911uxc_find_best_fit(lt6911uxc); |
|---|
| 947 | + lt6911uxc->cur_mode = mode; |
|---|
| 948 | + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, |
|---|
| 949 | + LT6911UXC_PIXEL_RATE); |
|---|
| 950 | + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, |
|---|
| 951 | + mode->mipi_freq_idx); |
|---|
| 952 | + |
|---|
| 851 | 953 | mutex_unlock(<6911uxc->confctl_mutex); |
|---|
| 852 | 954 | |
|---|
| 853 | 955 | v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field mode:%s\n", |
|---|
| .. | .. |
|---|
| 857 | 959 | return 0; |
|---|
| 858 | 960 | } |
|---|
| 859 | 961 | |
|---|
| 860 | | -static int lt6911uxc_get_reso_dist(const struct lt6911uxc_mode *mode, |
|---|
| 861 | | - struct v4l2_mbus_framefmt *framefmt) |
|---|
| 862 | | -{ |
|---|
| 863 | | - return abs(mode->width - framefmt->width) + |
|---|
| 864 | | - abs(mode->height - framefmt->height); |
|---|
| 865 | | -} |
|---|
| 866 | | - |
|---|
| 867 | | -static const struct lt6911uxc_mode * |
|---|
| 868 | | -lt6911uxc_find_best_fit(struct v4l2_subdev_format *fmt) |
|---|
| 869 | | -{ |
|---|
| 870 | | - struct v4l2_mbus_framefmt *framefmt = &fmt->format; |
|---|
| 871 | | - int dist; |
|---|
| 872 | | - int cur_best_fit = 0; |
|---|
| 873 | | - int cur_best_fit_dist = -1; |
|---|
| 874 | | - unsigned int i; |
|---|
| 875 | | - |
|---|
| 876 | | - for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { |
|---|
| 877 | | - dist = lt6911uxc_get_reso_dist(&supported_modes[i], framefmt); |
|---|
| 878 | | - if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { |
|---|
| 879 | | - cur_best_fit_dist = dist; |
|---|
| 880 | | - cur_best_fit = i; |
|---|
| 881 | | - } |
|---|
| 882 | | - } |
|---|
| 883 | | - |
|---|
| 884 | | - return &supported_modes[cur_best_fit]; |
|---|
| 885 | | -} |
|---|
| 886 | | - |
|---|
| 887 | 962 | static int lt6911uxc_set_fmt(struct v4l2_subdev *sd, |
|---|
| 888 | 963 | struct v4l2_subdev_pad_config *cfg, |
|---|
| 889 | 964 | struct v4l2_subdev_format *format) |
|---|
| 890 | 965 | { |
|---|
| 891 | 966 | struct lt6911uxc *lt6911uxc = to_state(sd); |
|---|
| 892 | 967 | const struct lt6911uxc_mode *mode; |
|---|
| 893 | | - int index; |
|---|
| 894 | 968 | |
|---|
| 895 | 969 | /* is overwritten by get_fmt */ |
|---|
| 896 | 970 | u32 code = format->format.code; |
|---|
| .. | .. |
|---|
| 913 | 987 | return 0; |
|---|
| 914 | 988 | |
|---|
| 915 | 989 | lt6911uxc->mbus_fmt_code = format->format.code; |
|---|
| 916 | | - mode = lt6911uxc_find_best_fit(format); |
|---|
| 990 | + mode = lt6911uxc_find_best_fit(lt6911uxc); |
|---|
| 917 | 991 | lt6911uxc->cur_mode = mode; |
|---|
| 918 | 992 | enable_stream(sd, false); |
|---|
| 919 | | - |
|---|
| 920 | | - if (((mode->width == 720) && (mode->height == 576)) || |
|---|
| 921 | | - ((mode->width == 720) && (mode->height == 480))) |
|---|
| 922 | | - index = 1; |
|---|
| 923 | | - else |
|---|
| 924 | | - index = 0; |
|---|
| 925 | | - |
|---|
| 926 | | - __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, index); |
|---|
| 927 | | - v4l2_dbg(1, debug, sd, "%s res wxh:%dx%d, link freq:%llu", __func__, |
|---|
| 928 | | - mode->width, mode->height, link_freq_menu_items[index]); |
|---|
| 929 | 993 | |
|---|
| 930 | 994 | return 0; |
|---|
| 931 | 995 | } |
|---|
| .. | .. |
|---|
| 955 | 1019 | static long lt6911uxc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) |
|---|
| 956 | 1020 | { |
|---|
| 957 | 1021 | struct lt6911uxc *lt6911uxc = to_state(sd); |
|---|
| 1022 | + struct device *dev = <6911uxc->i2c_client->dev; |
|---|
| 958 | 1023 | long ret = 0; |
|---|
| 1024 | + struct rkmodule_csi_dphy_param *dphy_param; |
|---|
| 1025 | + struct rkmodule_capture_info *capture_info; |
|---|
| 959 | 1026 | |
|---|
| 960 | 1027 | switch (cmd) { |
|---|
| 961 | 1028 | case RKMODULE_GET_MODULE_INFO: |
|---|
| .. | .. |
|---|
| 963 | 1030 | break; |
|---|
| 964 | 1031 | case RKMODULE_GET_HDMI_MODE: |
|---|
| 965 | 1032 | *(int *)arg = RKMODULE_HDMIIN_MODE; |
|---|
| 1033 | + break; |
|---|
| 1034 | + case RKMODULE_SET_CSI_DPHY_PARAM: |
|---|
| 1035 | + dphy_param = (struct rkmodule_csi_dphy_param *)arg; |
|---|
| 1036 | + if (dphy_param->vendor == PHY_VENDOR_SAMSUNG) |
|---|
| 1037 | + rk3588_dcphy_param = *dphy_param; |
|---|
| 1038 | + dev_dbg(<6911uxc->i2c_client->dev, |
|---|
| 1039 | + "sensor set dphy param\n"); |
|---|
| 1040 | + break; |
|---|
| 1041 | + case RKMODULE_GET_CSI_DPHY_PARAM: |
|---|
| 1042 | + dphy_param = (struct rkmodule_csi_dphy_param *)arg; |
|---|
| 1043 | + *dphy_param = rk3588_dcphy_param; |
|---|
| 1044 | + dev_dbg(<6911uxc->i2c_client->dev, |
|---|
| 1045 | + "sensor get dphy param\n"); |
|---|
| 1046 | + break; |
|---|
| 1047 | + case RKMODULE_GET_CAPTURE_MODE: |
|---|
| 1048 | + capture_info = (struct rkmodule_capture_info *)arg; |
|---|
| 1049 | + if (lt6911uxc->csi_lanes_in_use == 8) { |
|---|
| 1050 | + dev_info(dev, "8 lanes in use, set dual mipi mode\n"); |
|---|
| 1051 | + capture_info->mode = RKMODULE_MULTI_DEV_COMBINE_ONE; |
|---|
| 1052 | + capture_info->multi_dev = lt6911uxc->multi_dev_info; |
|---|
| 1053 | + } else { |
|---|
| 1054 | + capture_info->mode = 0; |
|---|
| 1055 | + } |
|---|
| 966 | 1056 | break; |
|---|
| 967 | 1057 | default: |
|---|
| 968 | 1058 | ret = -ENOIOCTLCMD; |
|---|
| .. | .. |
|---|
| 980 | 1070 | struct rkmodule_inf *inf; |
|---|
| 981 | 1071 | long ret; |
|---|
| 982 | 1072 | int *seq; |
|---|
| 1073 | + struct rkmodule_csi_dphy_param *dphy_param; |
|---|
| 1074 | + struct rkmodule_capture_info *capture_info; |
|---|
| 983 | 1075 | |
|---|
| 984 | 1076 | switch (cmd) { |
|---|
| 985 | 1077 | case RKMODULE_GET_MODULE_INFO: |
|---|
| .. | .. |
|---|
| 1011 | 1103 | ret = -EFAULT; |
|---|
| 1012 | 1104 | } |
|---|
| 1013 | 1105 | kfree(seq); |
|---|
| 1106 | + break; |
|---|
| 1107 | + case RKMODULE_SET_CSI_DPHY_PARAM: |
|---|
| 1108 | + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); |
|---|
| 1109 | + if (!dphy_param) { |
|---|
| 1110 | + ret = -ENOMEM; |
|---|
| 1111 | + return ret; |
|---|
| 1112 | + } |
|---|
| 1113 | + |
|---|
| 1114 | + ret = copy_from_user(dphy_param, up, sizeof(*dphy_param)); |
|---|
| 1115 | + if (!ret) |
|---|
| 1116 | + ret = lt6911uxc_ioctl(sd, cmd, dphy_param); |
|---|
| 1117 | + else |
|---|
| 1118 | + ret = -EFAULT; |
|---|
| 1119 | + kfree(dphy_param); |
|---|
| 1120 | + break; |
|---|
| 1121 | + case RKMODULE_GET_CSI_DPHY_PARAM: |
|---|
| 1122 | + dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL); |
|---|
| 1123 | + if (!dphy_param) { |
|---|
| 1124 | + ret = -ENOMEM; |
|---|
| 1125 | + return ret; |
|---|
| 1126 | + } |
|---|
| 1127 | + |
|---|
| 1128 | + ret = lt6911uxc_ioctl(sd, cmd, dphy_param); |
|---|
| 1129 | + if (!ret) { |
|---|
| 1130 | + ret = copy_to_user(up, dphy_param, sizeof(*dphy_param)); |
|---|
| 1131 | + if (ret) |
|---|
| 1132 | + ret = -EFAULT; |
|---|
| 1133 | + } |
|---|
| 1134 | + kfree(dphy_param); |
|---|
| 1135 | + break; |
|---|
| 1136 | + case RKMODULE_GET_CAPTURE_MODE: |
|---|
| 1137 | + capture_info = kzalloc(sizeof(*capture_info), GFP_KERNEL); |
|---|
| 1138 | + if (!capture_info) { |
|---|
| 1139 | + ret = -ENOMEM; |
|---|
| 1140 | + return ret; |
|---|
| 1141 | + } |
|---|
| 1142 | + |
|---|
| 1143 | + ret = lt6911uxc_ioctl(sd, cmd, capture_info); |
|---|
| 1144 | + if (!ret) { |
|---|
| 1145 | + ret = copy_to_user(up, capture_info, sizeof(*capture_info)); |
|---|
| 1146 | + if (ret) |
|---|
| 1147 | + ret = -EFAULT; |
|---|
| 1148 | + } |
|---|
| 1149 | + kfree(capture_info); |
|---|
| 1014 | 1150 | break; |
|---|
| 1015 | 1151 | default: |
|---|
| 1016 | 1152 | ret = -ENOIOCTLCMD; |
|---|
| .. | .. |
|---|
| 1091 | 1227 | |
|---|
| 1092 | 1228 | static int lt6911uxc_init_v4l2_ctrls(struct lt6911uxc *lt6911uxc) |
|---|
| 1093 | 1229 | { |
|---|
| 1230 | + const struct lt6911uxc_mode *mode; |
|---|
| 1094 | 1231 | struct v4l2_subdev *sd; |
|---|
| 1095 | 1232 | int ret; |
|---|
| 1096 | 1233 | |
|---|
| 1234 | + mode = lt6911uxc->cur_mode; |
|---|
| 1097 | 1235 | sd = <6911uxc->sd; |
|---|
| 1098 | 1236 | ret = v4l2_ctrl_handler_init(<6911uxc->hdl, 5); |
|---|
| 1099 | 1237 | if (ret) |
|---|
| .. | .. |
|---|
| 1103 | 1241 | V4L2_CID_LINK_FREQ, |
|---|
| 1104 | 1242 | ARRAY_SIZE(link_freq_menu_items) - 1, 0, |
|---|
| 1105 | 1243 | link_freq_menu_items); |
|---|
| 1106 | | - v4l2_ctrl_new_std(<6911uxc->hdl, NULL, V4L2_CID_PIXEL_RATE, |
|---|
| 1107 | | - 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); |
|---|
| 1244 | + lt6911uxc->pixel_rate = v4l2_ctrl_new_std(<6911uxc->hdl, NULL, |
|---|
| 1245 | + V4L2_CID_PIXEL_RATE, |
|---|
| 1246 | + 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); |
|---|
| 1108 | 1247 | |
|---|
| 1109 | 1248 | lt6911uxc->detect_tx_5v_ctrl = v4l2_ctrl_new_std(<6911uxc->hdl, |
|---|
| 1110 | 1249 | NULL, V4L2_CID_DV_RX_POWER_PRESENT, |
|---|
| .. | .. |
|---|
| 1122 | 1261 | v4l2_err(sd, "cfg v4l2 ctrls failed! ret:%d\n", ret); |
|---|
| 1123 | 1262 | return ret; |
|---|
| 1124 | 1263 | } |
|---|
| 1264 | + |
|---|
| 1265 | + __v4l2_ctrl_s_ctrl(lt6911uxc->link_freq, mode->mipi_freq_idx); |
|---|
| 1266 | + __v4l2_ctrl_s_ctrl_int64(lt6911uxc->pixel_rate, LT6911UXC_PIXEL_RATE); |
|---|
| 1125 | 1267 | |
|---|
| 1126 | 1268 | if (lt6911uxc_update_controls(sd)) { |
|---|
| 1127 | 1269 | ret = -ENODEV; |
|---|
| .. | .. |
|---|
| 1279 | 1421 | } |
|---|
| 1280 | 1422 | #endif |
|---|
| 1281 | 1423 | |
|---|
| 1424 | +static ssize_t audio_rate_show(struct device *dev, |
|---|
| 1425 | + struct device_attribute *attr, char *buf) |
|---|
| 1426 | +{ |
|---|
| 1427 | + struct lt6911uxc *lt6911uxc = dev_get_drvdata(dev); |
|---|
| 1428 | + |
|---|
| 1429 | + return snprintf(buf, PAGE_SIZE, "%d", lt6911uxc->audio_sampling_rate); |
|---|
| 1430 | +} |
|---|
| 1431 | + |
|---|
| 1432 | +static ssize_t audio_present_show(struct device *dev, |
|---|
| 1433 | + struct device_attribute *attr, char *buf) |
|---|
| 1434 | +{ |
|---|
| 1435 | + struct lt6911uxc *lt6911uxc = dev_get_drvdata(dev); |
|---|
| 1436 | + |
|---|
| 1437 | + return snprintf(buf, PAGE_SIZE, "%d", |
|---|
| 1438 | + tx_5v_power_present(<6911uxc->sd) ? |
|---|
| 1439 | + lt6911uxc->is_audio_present : 0); |
|---|
| 1440 | +} |
|---|
| 1441 | + |
|---|
| 1442 | +static DEVICE_ATTR_RO(audio_rate); |
|---|
| 1443 | +static DEVICE_ATTR_RO(audio_present); |
|---|
| 1444 | + |
|---|
| 1445 | +static struct attribute *lt6911_attrs[] = { |
|---|
| 1446 | + &dev_attr_audio_rate.attr, |
|---|
| 1447 | + &dev_attr_audio_present.attr, |
|---|
| 1448 | + NULL |
|---|
| 1449 | +}; |
|---|
| 1450 | +ATTRIBUTE_GROUPS(lt6911); |
|---|
| 1451 | + |
|---|
| 1452 | +static int lt6911uxc_get_multi_dev_info(struct lt6911uxc *lt6911uxc) |
|---|
| 1453 | +{ |
|---|
| 1454 | + struct device *dev = <6911uxc->i2c_client->dev; |
|---|
| 1455 | + struct device_node *node = dev->of_node; |
|---|
| 1456 | + struct device_node *multi_info_np; |
|---|
| 1457 | + |
|---|
| 1458 | + multi_info_np = of_get_child_by_name(node, "multi-dev-info"); |
|---|
| 1459 | + if (!multi_info_np) { |
|---|
| 1460 | + dev_info(dev, "failed to get multi dev info\n"); |
|---|
| 1461 | + return -EINVAL; |
|---|
| 1462 | + } |
|---|
| 1463 | + |
|---|
| 1464 | + of_property_read_u32(multi_info_np, "dev-idx-l", |
|---|
| 1465 | + <6911uxc->multi_dev_info.dev_idx[0]); |
|---|
| 1466 | + of_property_read_u32(multi_info_np, "dev-idx-r", |
|---|
| 1467 | + <6911uxc->multi_dev_info.dev_idx[1]); |
|---|
| 1468 | + of_property_read_u32(multi_info_np, "combine-idx", |
|---|
| 1469 | + <6911uxc->multi_dev_info.combine_idx[0]); |
|---|
| 1470 | + of_property_read_u32(multi_info_np, "pixel-offset", |
|---|
| 1471 | + <6911uxc->multi_dev_info.pixel_offset); |
|---|
| 1472 | + of_property_read_u32(multi_info_np, "dev-num", |
|---|
| 1473 | + <6911uxc->multi_dev_info.dev_num); |
|---|
| 1474 | + dev_info(dev, |
|---|
| 1475 | + "multi dev left: mipi%d, multi dev right: mipi%d, combile mipi%d, dev num: %d\n", |
|---|
| 1476 | + lt6911uxc->multi_dev_info.dev_idx[0], lt6911uxc->multi_dev_info.dev_idx[1], |
|---|
| 1477 | + lt6911uxc->multi_dev_info.combine_idx[0], lt6911uxc->multi_dev_info.dev_num); |
|---|
| 1478 | + |
|---|
| 1479 | + return 0; |
|---|
| 1480 | +} |
|---|
| 1481 | + |
|---|
| 1282 | 1482 | static int lt6911uxc_probe(struct i2c_client *client, |
|---|
| 1283 | 1483 | const struct i2c_device_id *id) |
|---|
| 1284 | 1484 | { |
|---|
| .. | .. |
|---|
| 1310 | 1510 | v4l2_err(sd, "lt6911uxc_parse_of failed! err:%d\n", err); |
|---|
| 1311 | 1511 | return err; |
|---|
| 1312 | 1512 | } |
|---|
| 1513 | + |
|---|
| 1514 | + err = lt6911uxc_get_multi_dev_info(lt6911uxc); |
|---|
| 1515 | + if (err) |
|---|
| 1516 | + v4l2_info(sd, "get multi dev info failed, not use dual mipi mode\n"); |
|---|
| 1313 | 1517 | |
|---|
| 1314 | 1518 | err = lt6911uxc_check_chip_id(lt6911uxc); |
|---|
| 1315 | 1519 | if (err < 0) |
|---|
| .. | .. |
|---|
| 1354 | 1558 | v4l2_err(sd, "v4l2 register subdev failed! err:%d\n", err); |
|---|
| 1355 | 1559 | goto err_clean_entity; |
|---|
| 1356 | 1560 | } |
|---|
| 1561 | + |
|---|
| 1562 | + lt6911uxc->classdev = device_create_with_groups(rk_hdmirx_class(), |
|---|
| 1563 | + dev, MKDEV(0, 0), |
|---|
| 1564 | + lt6911uxc, |
|---|
| 1565 | + lt6911_groups, |
|---|
| 1566 | + "lt6911"); |
|---|
| 1567 | + if (IS_ERR(lt6911uxc->classdev)) |
|---|
| 1568 | + goto err_clean_entity; |
|---|
| 1357 | 1569 | |
|---|
| 1358 | 1570 | INIT_DELAYED_WORK(<6911uxc->delayed_work_enable_hotplug, |
|---|
| 1359 | 1571 | lt6911uxc_delayed_work_enable_hotplug); |
|---|
| .. | .. |
|---|
| 1459 | 1671 | i2c_del_driver(<6911uxc_driver); |
|---|
| 1460 | 1672 | } |
|---|
| 1461 | 1673 | |
|---|
| 1462 | | -late_initcall(lt6911uxc_driver_init); |
|---|
| 1674 | +device_initcall_sync(lt6911uxc_driver_init); |
|---|
| 1463 | 1675 | module_exit(lt6911uxc_driver_exit); |
|---|
| 1464 | 1676 | |
|---|
| 1465 | 1677 | MODULE_DESCRIPTION("Lontium LT6911UXC HDMI to MIPI CSI-2 bridge driver"); |
|---|
| 1466 | 1678 | MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>"); |
|---|
| 1679 | +MODULE_AUTHOR("Jianwei Fan <jianwei.fan@rock-chips.com>"); |
|---|
| 1467 | 1680 | MODULE_LICENSE("GPL v2"); |
|---|