| .. | .. |
|---|
| 3 | 3 | * Copyright (c) 2021 Rockchip Electronics Co. Ltd. |
|---|
| 4 | 4 | * |
|---|
| 5 | 5 | * Author: Dingxian Wen <shawn.wen@rock-chips.com> |
|---|
| 6 | + * V0.0X01.0X00 first version. |
|---|
| 7 | + * V0.0X01.0X01 fix if plugin_gpio was not used. |
|---|
| 8 | + * V0.0X01.0X02 modify driver init level to late_initcall. |
|---|
| 6 | 9 | */ |
|---|
| 7 | 10 | |
|---|
| 8 | 11 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 21 | 24 | #include <linux/version.h> |
|---|
| 22 | 25 | #include <linux/videodev2.h> |
|---|
| 23 | 26 | #include <linux/workqueue.h> |
|---|
| 27 | +#include <linux/compat.h> |
|---|
| 24 | 28 | #include <media/v4l2-controls_rockchip.h> |
|---|
| 25 | 29 | #include <media/v4l2-ctrls.h> |
|---|
| 26 | 30 | #include <media/v4l2-device.h> |
|---|
| .. | .. |
|---|
| 29 | 33 | #include <media/v4l2-fwnode.h> |
|---|
| 30 | 34 | #include "lt6911uxc.h" |
|---|
| 31 | 35 | |
|---|
| 32 | | -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x0) |
|---|
| 36 | +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x2) |
|---|
| 33 | 37 | #define LT6911UXC_NAME "LT6911UXC" |
|---|
| 34 | 38 | |
|---|
| 35 | 39 | #define LT6911UXC_LINK_FREQ_HIGH 400000000 |
|---|
| .. | .. |
|---|
| 37 | 41 | #define LT6911UXC_PIXEL_RATE 400000000 |
|---|
| 38 | 42 | |
|---|
| 39 | 43 | #define I2C_MAX_XFER_SIZE 128 |
|---|
| 44 | + |
|---|
| 45 | +#ifdef LT6911UXC_OUT_RGB |
|---|
| 46 | +#define LT6911UXC_MEDIA_BUS_FMT MEDIA_BUS_FMT_BGR888_1X24 |
|---|
| 47 | +#else |
|---|
| 48 | +#define LT6911UXC_MEDIA_BUS_FMT MEDIA_BUS_FMT_UYVY8_2X8 |
|---|
| 49 | +#endif |
|---|
| 40 | 50 | |
|---|
| 41 | 51 | static int debug; |
|---|
| 42 | 52 | module_param(debug, int, 0644); |
|---|
| .. | .. |
|---|
| 286 | 296 | |
|---|
| 287 | 297 | static inline bool tx_5v_power_present(struct v4l2_subdev *sd) |
|---|
| 288 | 298 | { |
|---|
| 289 | | - int val; |
|---|
| 299 | + bool ret; |
|---|
| 300 | + int val, i, cnt; |
|---|
| 290 | 301 | struct lt6911uxc *lt6911uxc = to_state(sd); |
|---|
| 291 | 302 | |
|---|
| 292 | | - val = gpiod_get_value(lt6911uxc->plugin_det_gpio); |
|---|
| 293 | | - v4l2_dbg(1, debug, sd, "%s plug det: %s!\n", __func__, |
|---|
| 294 | | - (val > 0) ? "int" : "out"); |
|---|
| 303 | + /* if not use plugin det gpio */ |
|---|
| 304 | + if (!lt6911uxc->plugin_det_gpio) |
|---|
| 305 | + return true; |
|---|
| 295 | 306 | |
|---|
| 296 | | - return (val > 0); |
|---|
| 307 | + cnt = 0; |
|---|
| 308 | + for (i = 0; i < 5; i++) { |
|---|
| 309 | + val = gpiod_get_value(lt6911uxc->plugin_det_gpio); |
|---|
| 310 | + |
|---|
| 311 | + if (val > 0) |
|---|
| 312 | + cnt++; |
|---|
| 313 | + usleep_range(500, 600); |
|---|
| 314 | + } |
|---|
| 315 | + |
|---|
| 316 | + ret = (cnt >= 3) ? true : false; |
|---|
| 317 | + v4l2_dbg(1, debug, sd, "%s: %d\n", __func__, ret); |
|---|
| 318 | + |
|---|
| 319 | + return ret; |
|---|
| 297 | 320 | } |
|---|
| 298 | 321 | |
|---|
| 299 | 322 | static inline bool no_signal(struct v4l2_subdev *sd) |
|---|
| .. | .. |
|---|
| 505 | 528 | struct v4l2_subdev *sd = <6911uxc->sd; |
|---|
| 506 | 529 | |
|---|
| 507 | 530 | v4l2_dbg(2, debug, sd, "%s:\n", __func__); |
|---|
| 531 | + |
|---|
| 532 | + v4l2_ctrl_s_ctrl(lt6911uxc->detect_tx_5v_ctrl, tx_5v_power_present(sd)); |
|---|
| 508 | 533 | lt6911uxc_config_hpd(sd); |
|---|
| 509 | 534 | } |
|---|
| 510 | 535 | |
|---|
| .. | .. |
|---|
| 587 | 612 | |
|---|
| 588 | 613 | if (sd->devnode) |
|---|
| 589 | 614 | v4l2_subdev_notify_event(sd, <6911uxc_ev_fmt); |
|---|
| 590 | | -} |
|---|
| 591 | | - |
|---|
| 592 | | -static int lt6911uxc_get_ctrl(struct v4l2_ctrl *ctrl) |
|---|
| 593 | | -{ |
|---|
| 594 | | - int ret = -1; |
|---|
| 595 | | - struct lt6911uxc *lt6911uxc = container_of(ctrl->handler, |
|---|
| 596 | | - struct lt6911uxc, hdl); |
|---|
| 597 | | - struct v4l2_subdev *sd = &(lt6911uxc->sd); |
|---|
| 598 | | - |
|---|
| 599 | | - if (ctrl->id == V4L2_CID_DV_RX_POWER_PRESENT) { |
|---|
| 600 | | - ret = tx_5v_power_present(sd); |
|---|
| 601 | | - *ctrl->p_new.p_s32 = ret; |
|---|
| 602 | | - } |
|---|
| 603 | | - |
|---|
| 604 | | - return ret; |
|---|
| 605 | 615 | } |
|---|
| 606 | 616 | |
|---|
| 607 | 617 | static int lt6911uxc_isr(struct v4l2_subdev *sd, u32 status, bool *handled) |
|---|
| .. | .. |
|---|
| 738 | 748 | return 0; |
|---|
| 739 | 749 | } |
|---|
| 740 | 750 | |
|---|
| 741 | | -static int lt6911uxc_g_mbus_config(struct v4l2_subdev *sd, |
|---|
| 751 | +static int lt6911uxc_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad, |
|---|
| 742 | 752 | struct v4l2_mbus_config *cfg) |
|---|
| 743 | 753 | { |
|---|
| 744 | 754 | struct lt6911uxc *lt6911uxc = to_state(sd); |
|---|
| 745 | 755 | |
|---|
| 746 | | - cfg->type = V4L2_MBUS_CSI2; |
|---|
| 756 | + cfg->type = V4L2_MBUS_CSI2_DPHY; |
|---|
| 747 | 757 | cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_CHANNEL_0; |
|---|
| 748 | 758 | |
|---|
| 749 | 759 | switch (lt6911uxc->csi_lanes_in_use) { |
|---|
| .. | .. |
|---|
| 780 | 790 | { |
|---|
| 781 | 791 | switch (code->index) { |
|---|
| 782 | 792 | case 0: |
|---|
| 783 | | - code->code = MEDIA_BUS_FMT_UYVY8_2X8; |
|---|
| 793 | + code->code = LT6911UXC_MEDIA_BUS_FMT; |
|---|
| 784 | 794 | break; |
|---|
| 785 | 795 | |
|---|
| 786 | 796 | default: |
|---|
| .. | .. |
|---|
| 797 | 807 | if (fse->index >= ARRAY_SIZE(supported_modes)) |
|---|
| 798 | 808 | return -EINVAL; |
|---|
| 799 | 809 | |
|---|
| 800 | | - if (fse->code != MEDIA_BUS_FMT_UYVY8_2X8) |
|---|
| 810 | + if (fse->code != LT6911UXC_MEDIA_BUS_FMT) |
|---|
| 801 | 811 | return -EINVAL; |
|---|
| 802 | 812 | |
|---|
| 803 | 813 | fse->min_width = supported_modes[fse->index].width; |
|---|
| .. | .. |
|---|
| 815 | 825 | if (fie->index >= ARRAY_SIZE(supported_modes)) |
|---|
| 816 | 826 | return -EINVAL; |
|---|
| 817 | 827 | |
|---|
| 818 | | - if (fie->code != MEDIA_BUS_FMT_UYVY8_2X8) |
|---|
| 819 | | - return -EINVAL; |
|---|
| 828 | + fie->code = LT6911UXC_MEDIA_BUS_FMT; |
|---|
| 820 | 829 | |
|---|
| 821 | 830 | fie->width = supported_modes[fie->index].width; |
|---|
| 822 | 831 | fie->height = supported_modes[fie->index].height; |
|---|
| .. | .. |
|---|
| 893 | 902 | return ret; |
|---|
| 894 | 903 | |
|---|
| 895 | 904 | switch (code) { |
|---|
| 896 | | - case MEDIA_BUS_FMT_UYVY8_2X8: |
|---|
| 905 | + case LT6911UXC_MEDIA_BUS_FMT: |
|---|
| 897 | 906 | break; |
|---|
| 898 | 907 | |
|---|
| 899 | 908 | default: |
|---|
| .. | .. |
|---|
| 952 | 961 | case RKMODULE_GET_MODULE_INFO: |
|---|
| 953 | 962 | lt6911uxc_get_module_inf(lt6911uxc, (struct rkmodule_inf *)arg); |
|---|
| 954 | 963 | break; |
|---|
| 964 | + case RKMODULE_GET_HDMI_MODE: |
|---|
| 965 | + *(int *)arg = RKMODULE_HDMIIN_MODE; |
|---|
| 966 | + break; |
|---|
| 955 | 967 | default: |
|---|
| 956 | 968 | ret = -ENOIOCTLCMD; |
|---|
| 957 | 969 | break; |
|---|
| .. | .. |
|---|
| 967 | 979 | void __user *up = compat_ptr(arg); |
|---|
| 968 | 980 | struct rkmodule_inf *inf; |
|---|
| 969 | 981 | long ret; |
|---|
| 982 | + int *seq; |
|---|
| 970 | 983 | |
|---|
| 971 | 984 | switch (cmd) { |
|---|
| 972 | 985 | case RKMODULE_GET_MODULE_INFO: |
|---|
| .. | .. |
|---|
| 984 | 997 | } |
|---|
| 985 | 998 | kfree(inf); |
|---|
| 986 | 999 | break; |
|---|
| 1000 | + case RKMODULE_GET_HDMI_MODE: |
|---|
| 1001 | + seq = kzalloc(sizeof(*seq), GFP_KERNEL); |
|---|
| 1002 | + if (!seq) { |
|---|
| 1003 | + ret = -ENOMEM; |
|---|
| 1004 | + return ret; |
|---|
| 1005 | + } |
|---|
| 987 | 1006 | |
|---|
| 1007 | + ret = lt6911uxc_ioctl(sd, cmd, seq); |
|---|
| 1008 | + if (!ret) { |
|---|
| 1009 | + ret = copy_to_user(up, seq, sizeof(*seq)); |
|---|
| 1010 | + if (ret) |
|---|
| 1011 | + ret = -EFAULT; |
|---|
| 1012 | + } |
|---|
| 1013 | + kfree(seq); |
|---|
| 1014 | + break; |
|---|
| 988 | 1015 | default: |
|---|
| 989 | 1016 | ret = -ENOIOCTLCMD; |
|---|
| 990 | 1017 | break; |
|---|
| .. | .. |
|---|
| 993 | 1020 | return ret; |
|---|
| 994 | 1021 | } |
|---|
| 995 | 1022 | #endif |
|---|
| 996 | | - |
|---|
| 997 | | -static const struct v4l2_ctrl_ops lt6911uxc_ctrl_ops = { |
|---|
| 998 | | - .g_volatile_ctrl = lt6911uxc_get_ctrl, |
|---|
| 999 | | -}; |
|---|
| 1000 | 1023 | |
|---|
| 1001 | 1024 | static const struct v4l2_subdev_core_ops lt6911uxc_core_ops = { |
|---|
| 1002 | 1025 | .interrupt_service_routine = lt6911uxc_isr, |
|---|
| .. | .. |
|---|
| 1013 | 1036 | .s_dv_timings = lt6911uxc_s_dv_timings, |
|---|
| 1014 | 1037 | .g_dv_timings = lt6911uxc_g_dv_timings, |
|---|
| 1015 | 1038 | .query_dv_timings = lt6911uxc_query_dv_timings, |
|---|
| 1016 | | - .g_mbus_config = lt6911uxc_g_mbus_config, |
|---|
| 1017 | 1039 | .s_stream = lt6911uxc_s_stream, |
|---|
| 1018 | 1040 | .g_frame_interval = lt6911uxc_g_frame_interval, |
|---|
| 1019 | 1041 | }; |
|---|
| .. | .. |
|---|
| 1026 | 1048 | .get_fmt = lt6911uxc_get_fmt, |
|---|
| 1027 | 1049 | .enum_dv_timings = lt6911uxc_enum_dv_timings, |
|---|
| 1028 | 1050 | .dv_timings_cap = lt6911uxc_dv_timings_cap, |
|---|
| 1051 | + .get_mbus_config = lt6911uxc_g_mbus_config, |
|---|
| 1029 | 1052 | }; |
|---|
| 1030 | 1053 | |
|---|
| 1031 | 1054 | static const struct v4l2_subdev_ops lt6911uxc_ops = { |
|---|
| .. | .. |
|---|
| 1084 | 1107 | 0, LT6911UXC_PIXEL_RATE, 1, LT6911UXC_PIXEL_RATE); |
|---|
| 1085 | 1108 | |
|---|
| 1086 | 1109 | lt6911uxc->detect_tx_5v_ctrl = v4l2_ctrl_new_std(<6911uxc->hdl, |
|---|
| 1087 | | - <6911uxc_ctrl_ops, V4L2_CID_DV_RX_POWER_PRESENT, |
|---|
| 1110 | + NULL, V4L2_CID_DV_RX_POWER_PRESENT, |
|---|
| 1088 | 1111 | 0, 1, 0, 0); |
|---|
| 1089 | | - if (lt6911uxc->detect_tx_5v_ctrl) |
|---|
| 1090 | | - lt6911uxc->detect_tx_5v_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; |
|---|
| 1091 | 1112 | |
|---|
| 1092 | 1113 | lt6911uxc->audio_sampling_rate_ctrl = |
|---|
| 1093 | 1114 | v4l2_ctrl_new_custom(<6911uxc->hdl, |
|---|
| .. | .. |
|---|
| 1153 | 1174 | { |
|---|
| 1154 | 1175 | struct device *dev = <6911uxc->i2c_client->dev; |
|---|
| 1155 | 1176 | struct device_node *node = dev->of_node; |
|---|
| 1156 | | - struct v4l2_fwnode_endpoint *endpoint; |
|---|
| 1177 | + struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; |
|---|
| 1157 | 1178 | struct device_node *ep; |
|---|
| 1158 | 1179 | int ret; |
|---|
| 1159 | 1180 | |
|---|
| .. | .. |
|---|
| 1209 | 1230 | return ret; |
|---|
| 1210 | 1231 | } |
|---|
| 1211 | 1232 | |
|---|
| 1212 | | - endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep)); |
|---|
| 1213 | | - if (IS_ERR(endpoint)) { |
|---|
| 1233 | + ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); |
|---|
| 1234 | + if (ret) { |
|---|
| 1214 | 1235 | dev_err(dev, "failed to parse endpoint\n"); |
|---|
| 1215 | | - ret = PTR_ERR(endpoint); |
|---|
| 1216 | | - return ret; |
|---|
| 1236 | + goto put_node; |
|---|
| 1217 | 1237 | } |
|---|
| 1218 | 1238 | |
|---|
| 1219 | | - if (endpoint->bus_type != V4L2_MBUS_CSI2 || |
|---|
| 1220 | | - endpoint->bus.mipi_csi2.num_data_lanes == 0) { |
|---|
| 1239 | + if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || |
|---|
| 1240 | + endpoint.bus.mipi_csi2.num_data_lanes == 0) { |
|---|
| 1221 | 1241 | dev_err(dev, "missing CSI-2 properties in endpoint\n"); |
|---|
| 1222 | 1242 | ret = -EINVAL; |
|---|
| 1223 | 1243 | goto free_endpoint; |
|---|
| .. | .. |
|---|
| 1236 | 1256 | goto free_endpoint; |
|---|
| 1237 | 1257 | } |
|---|
| 1238 | 1258 | |
|---|
| 1239 | | - lt6911uxc->csi_lanes_in_use = endpoint->bus.mipi_csi2.num_data_lanes; |
|---|
| 1240 | | - lt6911uxc->bus = endpoint->bus.mipi_csi2; |
|---|
| 1259 | + lt6911uxc->csi_lanes_in_use = endpoint.bus.mipi_csi2.num_data_lanes; |
|---|
| 1260 | + lt6911uxc->bus = endpoint.bus.mipi_csi2; |
|---|
| 1241 | 1261 | lt6911uxc->enable_hdcp = false; |
|---|
| 1242 | 1262 | |
|---|
| 1243 | 1263 | gpiod_set_value(lt6911uxc->hpd_ctl_gpio, 0); |
|---|
| .. | .. |
|---|
| 1247 | 1267 | ret = 0; |
|---|
| 1248 | 1268 | |
|---|
| 1249 | 1269 | free_endpoint: |
|---|
| 1250 | | - v4l2_fwnode_endpoint_free(endpoint); |
|---|
| 1270 | + v4l2_fwnode_endpoint_free(&endpoint); |
|---|
| 1271 | +put_node: |
|---|
| 1272 | + of_node_put(ep); |
|---|
| 1251 | 1273 | return ret; |
|---|
| 1252 | 1274 | } |
|---|
| 1253 | 1275 | #else |
|---|
| .. | .. |
|---|
| 1260 | 1282 | static int lt6911uxc_probe(struct i2c_client *client, |
|---|
| 1261 | 1283 | const struct i2c_device_id *id) |
|---|
| 1262 | 1284 | { |
|---|
| 1285 | + struct v4l2_dv_timings default_timing = |
|---|
| 1286 | + V4L2_DV_BT_CEA_640X480P59_94; |
|---|
| 1263 | 1287 | struct lt6911uxc *lt6911uxc; |
|---|
| 1264 | 1288 | struct v4l2_subdev *sd; |
|---|
| 1265 | 1289 | struct device *dev = &client->dev; |
|---|
| .. | .. |
|---|
| 1277 | 1301 | |
|---|
| 1278 | 1302 | sd = <6911uxc->sd; |
|---|
| 1279 | 1303 | lt6911uxc->i2c_client = client; |
|---|
| 1304 | + lt6911uxc->timings = default_timing; |
|---|
| 1280 | 1305 | lt6911uxc->cur_mode = &supported_modes[0]; |
|---|
| 1281 | | - lt6911uxc->mbus_fmt_code = MEDIA_BUS_FMT_UYVY8_2X8; |
|---|
| 1306 | + lt6911uxc->mbus_fmt_code = LT6911UXC_MEDIA_BUS_FMT; |
|---|
| 1282 | 1307 | |
|---|
| 1283 | 1308 | err = lt6911uxc_parse_of(lt6911uxc); |
|---|
| 1284 | 1309 | if (err) { |
|---|
| .. | .. |
|---|
| 1353 | 1378 | } |
|---|
| 1354 | 1379 | |
|---|
| 1355 | 1380 | lt6911uxc->plugin_irq = gpiod_to_irq(lt6911uxc->plugin_det_gpio); |
|---|
| 1356 | | - if (lt6911uxc->plugin_irq < 0) { |
|---|
| 1357 | | - dev_err(dev, "failed to get plugin det irq\n"); |
|---|
| 1358 | | - err = lt6911uxc->plugin_irq; |
|---|
| 1359 | | - goto err_work_queues; |
|---|
| 1360 | | - } |
|---|
| 1381 | + if (lt6911uxc->plugin_irq < 0) |
|---|
| 1382 | + dev_err(dev, "failed to get plugin det irq, maybe no use\n"); |
|---|
| 1361 | 1383 | |
|---|
| 1362 | 1384 | err = devm_request_threaded_irq(dev, lt6911uxc->plugin_irq, NULL, |
|---|
| 1363 | 1385 | plugin_detect_irq_handler, IRQF_TRIGGER_FALLING | |
|---|
| 1364 | 1386 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "lt6911uxc", |
|---|
| 1365 | 1387 | lt6911uxc); |
|---|
| 1366 | | - if (err) { |
|---|
| 1367 | | - dev_err(dev, "failed to register plugin det irq (%d)\n", err); |
|---|
| 1368 | | - goto err_work_queues; |
|---|
| 1369 | | - } |
|---|
| 1388 | + if (err) |
|---|
| 1389 | + dev_err(dev, "failed to register plugin det irq (%d), maybe no use\n", err); |
|---|
| 1370 | 1390 | |
|---|
| 1371 | 1391 | err = v4l2_ctrl_handler_setup(sd->ctrl_handler); |
|---|
| 1372 | 1392 | if (err) { |
|---|
| .. | .. |
|---|
| 1439 | 1459 | i2c_del_driver(<6911uxc_driver); |
|---|
| 1440 | 1460 | } |
|---|
| 1441 | 1461 | |
|---|
| 1442 | | -device_initcall_sync(lt6911uxc_driver_init); |
|---|
| 1462 | +late_initcall(lt6911uxc_driver_init); |
|---|
| 1443 | 1463 | module_exit(lt6911uxc_driver_exit); |
|---|
| 1444 | 1464 | |
|---|
| 1445 | 1465 | MODULE_DESCRIPTION("Lontium LT6911UXC HDMI to MIPI CSI-2 bridge driver"); |
|---|