| .. | .. |
|---|
| 9 | 9 | * V0.0X01.0X03 fix gain range. |
|---|
| 10 | 10 | * V0.0X01.0X04 add enum_frame_interval function. |
|---|
| 11 | 11 | * V0.0X01.0X05 add quick stream on/off |
|---|
| 12 | + * V0.0X01.0X06 support thunder boot function. |
|---|
| 12 | 13 | */ |
|---|
| 13 | 14 | |
|---|
| 14 | 15 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 31 | 32 | #include <media/v4l2-ctrls.h> |
|---|
| 32 | 33 | #include <media/v4l2-subdev.h> |
|---|
| 33 | 34 | #include <linux/pinctrl/consumer.h> |
|---|
| 35 | +#include "../platform/rockchip/isp/rkisp_tb_helper.h" |
|---|
| 34 | 36 | |
|---|
| 35 | | -#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x05) |
|---|
| 37 | +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x06) |
|---|
| 36 | 38 | |
|---|
| 37 | 39 | #ifndef V4L2_CID_DIGITAL_GAIN |
|---|
| 38 | 40 | #define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN |
|---|
| .. | .. |
|---|
| 120 | 122 | |
|---|
| 121 | 123 | #define SC401AI_NUM_SUPPLIES ARRAY_SIZE(sc401ai_supply_names) |
|---|
| 122 | 124 | |
|---|
| 123 | | -enum sc401ai_max_pad { |
|---|
| 124 | | - PAD0, /* link to isp */ |
|---|
| 125 | | - PAD1, /* link to csi wr0 | hdr x2:L x3:M */ |
|---|
| 126 | | - PAD2, /* link to csi wr1 | hdr x3:L */ |
|---|
| 127 | | - PAD3, /* link to csi wr2 | hdr x2:M x3:S */ |
|---|
| 128 | | - PAD_MAX, |
|---|
| 129 | | -}; |
|---|
| 130 | | - |
|---|
| 131 | 125 | struct regval { |
|---|
| 132 | 126 | u16 addr; |
|---|
| 133 | 127 | u8 val; |
|---|
| .. | .. |
|---|
| 170 | 164 | struct v4l2_ctrl *pixel_rate; |
|---|
| 171 | 165 | struct v4l2_ctrl *link_freq; |
|---|
| 172 | 166 | struct mutex mutex; |
|---|
| 167 | + struct v4l2_fract cur_fps; |
|---|
| 173 | 168 | bool streaming; |
|---|
| 174 | 169 | bool power_on; |
|---|
| 175 | 170 | unsigned int lane_num; |
|---|
| .. | .. |
|---|
| 180 | 175 | const char *module_name; |
|---|
| 181 | 176 | const char *len_name; |
|---|
| 182 | 177 | u32 cur_vts; |
|---|
| 178 | + bool is_thunderboot; |
|---|
| 179 | + bool is_first_streamoff; |
|---|
| 183 | 180 | struct preisp_hdrae_exp_s init_hdrae_exp; |
|---|
| 184 | 181 | }; |
|---|
| 185 | 182 | |
|---|
| .. | .. |
|---|
| 454 | 451 | SC401AI_LINK_FREQ_630, |
|---|
| 455 | 452 | }; |
|---|
| 456 | 453 | |
|---|
| 454 | +static int __sc401ai_power_on(struct sc401ai *sc401ai); |
|---|
| 455 | + |
|---|
| 457 | 456 | /* Write registers up to 4 at a time */ |
|---|
| 458 | 457 | static int sc401ai_write_reg(struct i2c_client *client, u16 reg, |
|---|
| 459 | 458 | u32 len, u32 val) |
|---|
| .. | .. |
|---|
| 631 | 630 | DIG_Fine_gain_reg = abs(800 * gain / (Dcg_gainx100 * Coarse_gain * |
|---|
| 632 | 631 | DIG_gain) / ANA_Fine_gainx64); |
|---|
| 633 | 632 | |
|---|
| 633 | + if (sc401ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { |
|---|
| 634 | + sc401ai->is_thunderboot = false; |
|---|
| 635 | + __sc401ai_power_on(sc401ai); |
|---|
| 636 | + } |
|---|
| 637 | + |
|---|
| 634 | 638 | ret = sc401ai_write_reg(sc401ai->client, |
|---|
| 635 | 639 | SC401AI_REG_DIG_GAIN, |
|---|
| 636 | 640 | SC401AI_REG_VALUE_08BIT, |
|---|
| .. | .. |
|---|
| 709 | 713 | __v4l2_ctrl_modify_range(sc401ai->vblank, vblank_def, |
|---|
| 710 | 714 | SC401AI_VTS_MAX - mode->height, |
|---|
| 711 | 715 | 1, vblank_def); |
|---|
| 716 | + sc401ai->cur_fps = mode->max_fps; |
|---|
| 717 | + sc401ai->cur_vts = mode->vts_def; |
|---|
| 712 | 718 | } |
|---|
| 713 | 719 | |
|---|
| 714 | 720 | mutex_unlock(&sc401ai->mutex); |
|---|
| .. | .. |
|---|
| 800 | 806 | struct sc401ai *sc401ai = to_sc401ai(sd); |
|---|
| 801 | 807 | const struct sc401ai_mode *mode = sc401ai->cur_mode; |
|---|
| 802 | 808 | |
|---|
| 803 | | - mutex_lock(&sc401ai->mutex); |
|---|
| 804 | | - fi->interval = mode->max_fps; |
|---|
| 805 | | - mutex_unlock(&sc401ai->mutex); |
|---|
| 809 | + if (sc401ai->streaming) |
|---|
| 810 | + fi->interval = sc401ai->cur_fps; |
|---|
| 811 | + else |
|---|
| 812 | + fi->interval = mode->max_fps; |
|---|
| 806 | 813 | |
|---|
| 807 | 814 | return 0; |
|---|
| 808 | 815 | } |
|---|
| 809 | 816 | |
|---|
| 810 | 817 | static int sc401ai_g_mbus_config(struct v4l2_subdev *sd, |
|---|
| 811 | | - struct v4l2_mbus_config *config) |
|---|
| 818 | + unsigned int pad_id, |
|---|
| 819 | + struct v4l2_mbus_config *config) |
|---|
| 812 | 820 | { |
|---|
| 813 | 821 | struct sc401ai *sc401ai = to_sc401ai(sd); |
|---|
| 814 | 822 | const struct sc401ai_mode *mode = sc401ai->cur_mode; |
|---|
| .. | .. |
|---|
| 823 | 831 | if (mode->hdr_mode == HDR_X3) |
|---|
| 824 | 832 | val |= V4L2_MBUS_CSI2_CHANNEL_2; |
|---|
| 825 | 833 | |
|---|
| 826 | | - config->type = V4L2_MBUS_CSI2; |
|---|
| 834 | + config->type = V4L2_MBUS_CSI2_DPHY; |
|---|
| 827 | 835 | config->flags = val; |
|---|
| 828 | 836 | |
|---|
| 829 | 837 | return 0; |
|---|
| .. | .. |
|---|
| 833 | 841 | struct rkmodule_inf *inf) |
|---|
| 834 | 842 | { |
|---|
| 835 | 843 | memset(inf, 0, sizeof(*inf)); |
|---|
| 836 | | - strlcpy(inf->base.sensor, SC401AI_NAME, sizeof(inf->base.sensor)); |
|---|
| 837 | | - strlcpy(inf->base.module, sc401ai->module_name, |
|---|
| 844 | + strscpy(inf->base.sensor, SC401AI_NAME, sizeof(inf->base.sensor)); |
|---|
| 845 | + strscpy(inf->base.module, sc401ai->module_name, |
|---|
| 838 | 846 | sizeof(inf->base.module)); |
|---|
| 839 | | - strlcpy(inf->base.lens, sc401ai->len_name, sizeof(inf->base.lens)); |
|---|
| 847 | + strscpy(inf->base.lens, sc401ai->len_name, sizeof(inf->base.lens)); |
|---|
| 840 | 848 | } |
|---|
| 841 | 849 | |
|---|
| 842 | 850 | static long sc401ai_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) |
|---|
| .. | .. |
|---|
| 992 | 1000 | { |
|---|
| 993 | 1001 | int ret; |
|---|
| 994 | 1002 | |
|---|
| 995 | | - ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list); |
|---|
| 996 | | - if (ret) |
|---|
| 997 | | - return ret; |
|---|
| 1003 | + if (!sc401ai->is_thunderboot) { |
|---|
| 1004 | + ret = sc401ai_write_array(sc401ai->client, sc401ai->cur_mode->reg_list); |
|---|
| 1005 | + if (ret) |
|---|
| 1006 | + return ret; |
|---|
| 998 | 1007 | |
|---|
| 999 | | - /* In case these controls are set before streaming */ |
|---|
| 1000 | | - ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler); |
|---|
| 1001 | | - if (ret) |
|---|
| 1002 | | - return ret; |
|---|
| 1008 | + /* In case these controls are set before streaming */ |
|---|
| 1009 | + ret = __v4l2_ctrl_handler_setup(&sc401ai->ctrl_handler); |
|---|
| 1010 | + if (ret) |
|---|
| 1011 | + return ret; |
|---|
| 1012 | + } |
|---|
| 1003 | 1013 | |
|---|
| 1004 | 1014 | return sc401ai_write_reg(sc401ai->client, |
|---|
| 1005 | | - SC401AI_REG_CTRL_MODE, |
|---|
| 1006 | | - SC401AI_REG_VALUE_08BIT, |
|---|
| 1007 | | - SC401AI_MODE_STREAMING); |
|---|
| 1015 | + SC401AI_REG_CTRL_MODE, |
|---|
| 1016 | + SC401AI_REG_VALUE_08BIT, |
|---|
| 1017 | + SC401AI_MODE_STREAMING); |
|---|
| 1018 | + |
|---|
| 1008 | 1019 | } |
|---|
| 1009 | 1020 | |
|---|
| 1010 | 1021 | static int __sc401ai_stop_stream(struct sc401ai *sc401ai) |
|---|
| 1011 | 1022 | { |
|---|
| 1023 | + if (sc401ai->is_thunderboot) { |
|---|
| 1024 | + sc401ai->is_first_streamoff = true; |
|---|
| 1025 | + pm_runtime_put(&sc401ai->client->dev); |
|---|
| 1026 | + } |
|---|
| 1027 | + |
|---|
| 1012 | 1028 | return sc401ai_write_reg(sc401ai->client, |
|---|
| 1013 | 1029 | SC401AI_REG_CTRL_MODE, |
|---|
| 1014 | 1030 | SC401AI_REG_VALUE_08BIT, |
|---|
| .. | .. |
|---|
| 1027 | 1043 | goto unlock_and_return; |
|---|
| 1028 | 1044 | |
|---|
| 1029 | 1045 | if (on) { |
|---|
| 1046 | + if (sc401ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) { |
|---|
| 1047 | + sc401ai->is_thunderboot = false; |
|---|
| 1048 | + __sc401ai_power_on(sc401ai); |
|---|
| 1049 | + } |
|---|
| 1030 | 1050 | ret = pm_runtime_get_sync(&client->dev); |
|---|
| 1031 | 1051 | if (ret < 0) { |
|---|
| 1032 | 1052 | pm_runtime_put_noidle(&client->dev); |
|---|
| .. | .. |
|---|
| 1118 | 1138 | dev_err(dev, "Failed to enable xvclk\n"); |
|---|
| 1119 | 1139 | return ret; |
|---|
| 1120 | 1140 | } |
|---|
| 1141 | + |
|---|
| 1142 | + if (sc401ai->is_thunderboot) |
|---|
| 1143 | + return 0; |
|---|
| 1144 | + |
|---|
| 1121 | 1145 | if (!IS_ERR(sc401ai->reset_gpio)) |
|---|
| 1122 | 1146 | gpiod_set_value_cansleep(sc401ai->reset_gpio, 0); |
|---|
| 1123 | 1147 | |
|---|
| .. | .. |
|---|
| 1155 | 1179 | { |
|---|
| 1156 | 1180 | int ret; |
|---|
| 1157 | 1181 | struct device *dev = &sc401ai->client->dev; |
|---|
| 1182 | + |
|---|
| 1183 | + if (sc401ai->is_thunderboot) { |
|---|
| 1184 | + if (sc401ai->is_first_streamoff) { |
|---|
| 1185 | + sc401ai->is_thunderboot = false; |
|---|
| 1186 | + sc401ai->is_first_streamoff = false; |
|---|
| 1187 | + } else { |
|---|
| 1188 | + return; |
|---|
| 1189 | + } |
|---|
| 1190 | + } |
|---|
| 1158 | 1191 | |
|---|
| 1159 | 1192 | if (!IS_ERR(sc401ai->pwdn_gpio)) |
|---|
| 1160 | 1193 | gpiod_set_value_cansleep(sc401ai->pwdn_gpio, 0); |
|---|
| .. | .. |
|---|
| 1249 | 1282 | static const struct v4l2_subdev_video_ops sc401ai_video_ops = { |
|---|
| 1250 | 1283 | .s_stream = sc401ai_s_stream, |
|---|
| 1251 | 1284 | .g_frame_interval = sc401ai_g_frame_interval, |
|---|
| 1252 | | - .g_mbus_config = sc401ai_g_mbus_config, |
|---|
| 1253 | 1285 | }; |
|---|
| 1254 | 1286 | |
|---|
| 1255 | 1287 | static const struct v4l2_subdev_pad_ops sc401ai_pad_ops = { |
|---|
| .. | .. |
|---|
| 1258 | 1290 | .enum_frame_interval = sc401ai_enum_frame_interval, |
|---|
| 1259 | 1291 | .get_fmt = sc401ai_get_fmt, |
|---|
| 1260 | 1292 | .set_fmt = sc401ai_set_fmt, |
|---|
| 1293 | + .get_mbus_config = sc401ai_g_mbus_config, |
|---|
| 1261 | 1294 | }; |
|---|
| 1262 | 1295 | |
|---|
| 1263 | 1296 | static const struct v4l2_subdev_ops sc401ai_subdev_ops = { |
|---|
| .. | .. |
|---|
| 1265 | 1298 | .video = &sc401ai_video_ops, |
|---|
| 1266 | 1299 | .pad = &sc401ai_pad_ops, |
|---|
| 1267 | 1300 | }; |
|---|
| 1301 | + |
|---|
| 1302 | +static void sc401ai_modify_fps_info(struct sc401ai *sc401ai) |
|---|
| 1303 | +{ |
|---|
| 1304 | + const struct sc401ai_mode *mode = sc401ai->cur_mode; |
|---|
| 1305 | + |
|---|
| 1306 | + sc401ai->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def / |
|---|
| 1307 | + sc401ai->cur_vts; |
|---|
| 1308 | +} |
|---|
| 1268 | 1309 | |
|---|
| 1269 | 1310 | static int sc401ai_set_ctrl(struct v4l2_ctrl *ctrl) |
|---|
| 1270 | 1311 | { |
|---|
| .. | .. |
|---|
| 1324 | 1365 | SC401AI_REG_VALUE_08BIT, |
|---|
| 1325 | 1366 | (ctrl->val + sc401ai->cur_mode->height) |
|---|
| 1326 | 1367 | & 0xff); |
|---|
| 1327 | | - sc401ai->cur_vts = ctrl->val + sc401ai->cur_mode->height; |
|---|
| 1368 | + if (!ret) |
|---|
| 1369 | + sc401ai->cur_vts = ctrl->val + sc401ai->cur_mode->height; |
|---|
| 1370 | + sc401ai_modify_fps_info(sc401ai); |
|---|
| 1328 | 1371 | break; |
|---|
| 1329 | 1372 | case V4L2_CID_TEST_PATTERN: |
|---|
| 1330 | 1373 | ret = sc401ai_enable_test_pattern(sc401ai, ctrl->val); |
|---|
| .. | .. |
|---|
| 1466 | 1509 | } |
|---|
| 1467 | 1510 | |
|---|
| 1468 | 1511 | sc401ai->subdev.ctrl_handler = handler; |
|---|
| 1512 | + sc401ai->cur_fps = mode->max_fps; |
|---|
| 1513 | + sc401ai->cur_vts = mode->vts_def; |
|---|
| 1469 | 1514 | |
|---|
| 1470 | 1515 | return 0; |
|---|
| 1471 | 1516 | |
|---|
| .. | .. |
|---|
| 1481 | 1526 | struct device *dev = &sc401ai->client->dev; |
|---|
| 1482 | 1527 | u32 id = 0; |
|---|
| 1483 | 1528 | int ret; |
|---|
| 1529 | + |
|---|
| 1530 | + if (sc401ai->is_thunderboot) { |
|---|
| 1531 | + dev_info(dev, "Enable thunderboot mode, skip sensor id check\n"); |
|---|
| 1532 | + return 0; |
|---|
| 1533 | + } |
|---|
| 1484 | 1534 | |
|---|
| 1485 | 1535 | ret = sc401ai_read_reg(client, SC401AI_REG_CHIP_ID, |
|---|
| 1486 | 1536 | SC401AI_REG_VALUE_16BIT, &id); |
|---|
| .. | .. |
|---|
| 1540 | 1590 | return -EINVAL; |
|---|
| 1541 | 1591 | } |
|---|
| 1542 | 1592 | |
|---|
| 1593 | + sc401ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); |
|---|
| 1594 | + |
|---|
| 1543 | 1595 | sc401ai->client = client; |
|---|
| 1544 | 1596 | for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { |
|---|
| 1545 | 1597 | if (hdr_mode == supported_modes[i].hdr_mode) { |
|---|
| .. | .. |
|---|
| 1556 | 1608 | return -EINVAL; |
|---|
| 1557 | 1609 | } |
|---|
| 1558 | 1610 | |
|---|
| 1559 | | - sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); |
|---|
| 1560 | | - if (IS_ERR(sc401ai->reset_gpio)) |
|---|
| 1561 | | - dev_warn(dev, "Failed to get reset-gpios\n"); |
|---|
| 1611 | + if (sc401ai->is_thunderboot) { |
|---|
| 1612 | + sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS); |
|---|
| 1613 | + if (IS_ERR(sc401ai->reset_gpio)) |
|---|
| 1614 | + dev_warn(dev, "Failed to get reset-gpios\n"); |
|---|
| 1562 | 1615 | |
|---|
| 1563 | | - sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); |
|---|
| 1564 | | - if (IS_ERR(sc401ai->pwdn_gpio)) |
|---|
| 1565 | | - dev_warn(dev, "Failed to get pwdn-gpios\n"); |
|---|
| 1616 | + sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS); |
|---|
| 1617 | + if (IS_ERR(sc401ai->pwdn_gpio)) |
|---|
| 1618 | + dev_warn(dev, "Failed to get pwdn-gpios\n"); |
|---|
| 1619 | + } else { |
|---|
| 1620 | + sc401ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); |
|---|
| 1621 | + if (IS_ERR(sc401ai->reset_gpio)) |
|---|
| 1622 | + dev_warn(dev, "Failed to get reset-gpios\n"); |
|---|
| 1623 | + |
|---|
| 1624 | + sc401ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); |
|---|
| 1625 | + if (IS_ERR(sc401ai->pwdn_gpio)) |
|---|
| 1626 | + dev_warn(dev, "Failed to get pwdn-gpios\n"); |
|---|
| 1627 | + } |
|---|
| 1566 | 1628 | |
|---|
| 1567 | 1629 | sc401ai->pinctrl = devm_pinctrl_get(dev); |
|---|
| 1568 | 1630 | if (!IS_ERR(sc401ai->pinctrl)) { |
|---|
| .. | .. |
|---|
| 1637 | 1699 | |
|---|
| 1638 | 1700 | pm_runtime_set_active(dev); |
|---|
| 1639 | 1701 | pm_runtime_enable(dev); |
|---|
| 1640 | | - pm_runtime_idle(dev); |
|---|
| 1702 | + if (sc401ai->is_thunderboot) |
|---|
| 1703 | + pm_runtime_get_sync(dev); |
|---|
| 1704 | + else |
|---|
| 1705 | + pm_runtime_idle(dev); |
|---|
| 1641 | 1706 | |
|---|
| 1642 | 1707 | return 0; |
|---|
| 1643 | 1708 | |
|---|
| .. | .. |
|---|
| 1709 | 1774 | i2c_del_driver(&sc401ai_i2c_driver); |
|---|
| 1710 | 1775 | } |
|---|
| 1711 | 1776 | |
|---|
| 1777 | +#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC) |
|---|
| 1778 | +subsys_initcall(sensor_mod_init); |
|---|
| 1779 | +#else |
|---|
| 1712 | 1780 | device_initcall_sync(sensor_mod_init); |
|---|
| 1781 | +#endif |
|---|
| 1782 | + |
|---|
| 1713 | 1783 | module_exit(sensor_mod_exit); |
|---|
| 1714 | 1784 | |
|---|
| 1715 | 1785 | MODULE_DESCRIPTION("smartsens sc401ai sensor driver"); |
|---|
| 1716 | | -MODULE_LICENSE("GPL v2"); |
|---|
| 1786 | +MODULE_LICENSE("GPL"); |
|---|