| .. | .. |
|---|
| 61 | 61 | #define SC230AI_REG_SEXPOSURE_L 0x3e05 |
|---|
| 62 | 62 | #define SC230AI_EXPOSURE_MIN 1 |
|---|
| 63 | 63 | #define SC230AI_EXPOSURE_STEP 1 |
|---|
| 64 | | -#define SC230AI_EXPOSURE_LIN_MAX (2 * 0x465 - 9) |
|---|
| 65 | | -#define SC230AI_EXPOSURE_HDR_MAX_S (2 * 0x465 - 9) |
|---|
| 66 | | -#define SC230AI_EXPOSURE_HDR_MAX_L (2 * 0x465 - 9) |
|---|
| 67 | 64 | #define SC230AI_VTS_MAX 0x7fff |
|---|
| 68 | 65 | |
|---|
| 69 | 66 | #define SC230AI_REG_DIG_GAIN 0x3e06 |
|---|
| .. | .. |
|---|
| 74 | 71 | #define SC230AI_REG_SANA_GAIN 0x3e12 |
|---|
| 75 | 72 | #define SC230AI_REG_SANA_FINE_GAIN 0x3e13 |
|---|
| 76 | 73 | #define SC230AI_GAIN_MIN 1000 |
|---|
| 77 | | -#define SC230AI_GAIN_MAX 1722628 //108.512*15.875*1000 |
|---|
| 74 | +#define SC230AI_GAIN_MAX 1574800 // 99.2*15.875*1000 |
|---|
| 78 | 75 | #define SC230AI_GAIN_STEP 1 |
|---|
| 79 | 76 | #define SC230AI_GAIN_DEFAULT 1000 |
|---|
| 80 | 77 | #define SC230AI_LGAIN 0 |
|---|
| .. | .. |
|---|
| 176 | 173 | const char *module_facing; |
|---|
| 177 | 174 | const char *module_name; |
|---|
| 178 | 175 | const char *len_name; |
|---|
| 176 | + enum rkmodule_sync_mode sync_mode; |
|---|
| 179 | 177 | u32 cur_vts; |
|---|
| 180 | 178 | bool has_init_exp; |
|---|
| 181 | 179 | bool is_thunderboot; |
|---|
| .. | .. |
|---|
| 537 | 535 | {REG_NULL, 0x00}, |
|---|
| 538 | 536 | }; |
|---|
| 539 | 537 | |
|---|
| 538 | +static __maybe_unused const struct regval sc230ai_interal_sync_master_start_regs[] = { |
|---|
| 539 | + {0x300a, 0x24}, //sync as output PAD |
|---|
| 540 | + {0x3032, 0xa0}, |
|---|
| 541 | + {0x3222, 0x00}, //master mode |
|---|
| 542 | + {REG_NULL, 0x00}, |
|---|
| 543 | +}; |
|---|
| 544 | + |
|---|
| 545 | +static __maybe_unused const struct regval sc230ai_interal_sync_master_stop_regs[] = { |
|---|
| 546 | + {REG_NULL, 0x00}, |
|---|
| 547 | +}; |
|---|
| 548 | + |
|---|
| 549 | +static __maybe_unused const struct regval sc230ai_interal_sync_slaver_start_regs[] = { |
|---|
| 550 | + {0x300a, 0x20}, //sync as input PAD |
|---|
| 551 | + {0x3222, 0x01}, //slave mode |
|---|
| 552 | + {0x3224, 0x92}, //fsync trigger |
|---|
| 553 | + {0x3614, 0x01}, |
|---|
| 554 | + {REG_NULL, 0x00}, |
|---|
| 555 | +}; |
|---|
| 556 | + |
|---|
| 557 | +static __maybe_unused const struct regval sc230ai_interal_sync_slaver_stop_regs[] = { |
|---|
| 558 | + {REG_NULL, 0x00}, |
|---|
| 559 | +}; |
|---|
| 540 | 560 | |
|---|
| 541 | 561 | static const struct sc230ai_mode supported_modes[] = { |
|---|
| 542 | 562 | { |
|---|
| .. | .. |
|---|
| 678 | 698 | *again = 0x00; |
|---|
| 679 | 699 | *dgain = 0x00; |
|---|
| 680 | 700 | *dgain_fine = total_gain * 128 / 1000; |
|---|
| 681 | | - } else if (total_gain < 3391) { /* 2 ~ 3.391 gain*/ |
|---|
| 701 | + } else if (total_gain < 3100) { /* 2 ~ 3.1 gain*/ |
|---|
| 682 | 702 | *again = 0x01; |
|---|
| 683 | 703 | *dgain = 0x00; |
|---|
| 684 | 704 | *dgain_fine = total_gain * 128 / 1000 / 2; |
|---|
| 685 | | - } else if (total_gain < 3391 * 2) { /* 3.391 ~ 6.782 gain*/ |
|---|
| 705 | + } else if (total_gain < 3100 * 2) { /* 3.100 ~ 6.200 gain*/ |
|---|
| 686 | 706 | *again = 0x40; |
|---|
| 687 | 707 | *dgain = 0x00; |
|---|
| 688 | | - *dgain_fine = total_gain * 128 / 3391; |
|---|
| 689 | | - } else if (total_gain < 3391 * 4) { /* 6.782 ~ 13.564 gain*/ |
|---|
| 708 | + *dgain_fine = total_gain * 128 / 3100; |
|---|
| 709 | + } else if (total_gain < 3100 * 4) { /* 6.200 ~ 12.400 gain*/ |
|---|
| 690 | 710 | *again = 0x48; |
|---|
| 691 | 711 | *dgain = 0x00; |
|---|
| 692 | | - *dgain_fine = total_gain * 128 / 3391 / 2; |
|---|
| 693 | | - } else if (total_gain < 3391 * 8) { /* 13.564 ~ 27.128 gain*/ |
|---|
| 712 | + *dgain_fine = total_gain * 128 / 3100 / 2; |
|---|
| 713 | + } else if (total_gain < 3100 * 8) { /* 12.400 ~ 24.800 gain*/ |
|---|
| 694 | 714 | *again = 0x49; |
|---|
| 695 | 715 | *dgain = 0x00; |
|---|
| 696 | | - *dgain_fine = total_gain * 128 / 3391 / 4; |
|---|
| 697 | | - } else if (total_gain < 3391 * 16) { /* 27.128 ~ 54.256 gain*/ |
|---|
| 716 | + *dgain_fine = total_gain * 128 / 3100 / 4; |
|---|
| 717 | + } else if (total_gain < 3100 * 16) { /* 24.800 ~ 49.600 gain*/ |
|---|
| 698 | 718 | *again = 0x4b; |
|---|
| 699 | 719 | *dgain = 0x00; |
|---|
| 700 | | - *dgain_fine = total_gain * 128 / 3391 / 8; |
|---|
| 701 | | - } else if (total_gain < 3391 * 32) { /* 54.256 ~ 108.512 gain*/ |
|---|
| 720 | + *dgain_fine = total_gain * 128 / 3100 / 8; |
|---|
| 721 | + } else if (total_gain < 3100 * 32) { /* 49.600 ~ 99.200 gain*/ |
|---|
| 702 | 722 | *again = 0x4f; |
|---|
| 703 | 723 | *dgain = 0x00; |
|---|
| 704 | | - *dgain_fine = total_gain * 128 / 3391 / 16; |
|---|
| 705 | | - } else if (total_gain < 3391 * 64) { /* 108.512 ~ 217.024 gain*/ |
|---|
| 724 | + *dgain_fine = total_gain * 128 / 3100 / 16; |
|---|
| 725 | + } else if (total_gain < 3100 * 64) { /* 99.200 ~ 198.400 gain*/ |
|---|
| 706 | 726 | *again = 0x5f; |
|---|
| 707 | 727 | *dgain = 0x00; |
|---|
| 708 | | - *dgain_fine = total_gain * 128 / 3391 / 32; |
|---|
| 709 | | - } else if (total_gain < 3391 * 128) { /* 217.024 ~ 434.048 gain*/ |
|---|
| 728 | + *dgain_fine = total_gain * 128 / 3100 / 32; |
|---|
| 729 | + } else if (total_gain < 3100 * 128) { /* 198.400 ~ 396.800 gain*/ |
|---|
| 710 | 730 | *again = 0x5f; |
|---|
| 711 | 731 | *dgain = 0x01; |
|---|
| 712 | | - *dgain_fine = total_gain * 128 / 3391 / 64; |
|---|
| 713 | | - } else if (total_gain < 3391 * 256) { /* 434.048 ~ 868.096 gain*/ |
|---|
| 732 | + *dgain_fine = total_gain * 128 / 3100 / 64; |
|---|
| 733 | + } else if (total_gain < 3100 * 256) { /* 396.800 ~ 793.600 gain*/ |
|---|
| 714 | 734 | *again = 0x5f; |
|---|
| 715 | 735 | *dgain = 0x03; |
|---|
| 716 | | - *dgain_fine = total_gain * 128 / 3391 / 128; |
|---|
| 717 | | - } else if (total_gain < 3391 * 512) { /* 868.096 ~ 1736.192 gain*/ |
|---|
| 736 | + *dgain_fine = total_gain * 128 / 3100 / 128; |
|---|
| 737 | + } else { /* 793.600 ~ 1587.200 gain*/ |
|---|
| 718 | 738 | *again = 0x5f; |
|---|
| 719 | 739 | *dgain = 0x07; |
|---|
| 720 | | - *dgain_fine = total_gain * 128 / 3391 / 128; |
|---|
| 740 | + *dgain_fine = total_gain * 128 / 3100 / 128; |
|---|
| 721 | 741 | } |
|---|
| 722 | 742 | |
|---|
| 723 | 743 | return ret; |
|---|
| .. | .. |
|---|
| 933 | 953 | u32 i, h, w; |
|---|
| 934 | 954 | long ret = 0; |
|---|
| 935 | 955 | u32 stream = 0; |
|---|
| 956 | + u32 *sync_mode = NULL; |
|---|
| 936 | 957 | |
|---|
| 937 | 958 | switch (cmd) { |
|---|
| 938 | 959 | case RKMODULE_GET_MODULE_INFO: |
|---|
| .. | .. |
|---|
| 983 | 1004 | ret = sc230ai_write_reg(sc230ai->client, SC230AI_REG_CTRL_MODE, |
|---|
| 984 | 1005 | SC230AI_REG_VALUE_08BIT, SC230AI_MODE_SW_STANDBY); |
|---|
| 985 | 1006 | break; |
|---|
| 1007 | + case RKMODULE_GET_SYNC_MODE: |
|---|
| 1008 | + sync_mode = (u32 *)arg; |
|---|
| 1009 | + *sync_mode = sc230ai->sync_mode; |
|---|
| 1010 | + break; |
|---|
| 1011 | + case RKMODULE_SET_SYNC_MODE: |
|---|
| 1012 | + sync_mode = (u32 *)arg; |
|---|
| 1013 | + sc230ai->sync_mode = *sync_mode; |
|---|
| 1014 | + break; |
|---|
| 986 | 1015 | default: |
|---|
| 987 | 1016 | ret = -ENOIOCTLCMD; |
|---|
| 988 | 1017 | break; |
|---|
| .. | .. |
|---|
| 1001 | 1030 | struct preisp_hdrae_exp_s *hdrae; |
|---|
| 1002 | 1031 | long ret; |
|---|
| 1003 | 1032 | u32 stream = 0; |
|---|
| 1033 | + u32 sync_mode; |
|---|
| 1004 | 1034 | |
|---|
| 1005 | 1035 | switch (cmd) { |
|---|
| 1006 | 1036 | case RKMODULE_GET_MODULE_INFO: |
|---|
| .. | .. |
|---|
| 1069 | 1099 | |
|---|
| 1070 | 1100 | ret = sc230ai_ioctl(sd, cmd, &stream); |
|---|
| 1071 | 1101 | break; |
|---|
| 1102 | + case RKMODULE_GET_SYNC_MODE: |
|---|
| 1103 | + ret = sc230ai_ioctl(sd, cmd, &sync_mode); |
|---|
| 1104 | + if (!ret) { |
|---|
| 1105 | + ret = copy_to_user(up, &sync_mode, sizeof(u32)); |
|---|
| 1106 | + if (ret) |
|---|
| 1107 | + ret = -EFAULT; |
|---|
| 1108 | + } |
|---|
| 1109 | + break; |
|---|
| 1110 | + case RKMODULE_SET_SYNC_MODE: |
|---|
| 1111 | + ret = copy_from_user(&sync_mode, up, sizeof(u32)); |
|---|
| 1112 | + if (!ret) |
|---|
| 1113 | + ret = sc230ai_ioctl(sd, cmd, &sync_mode); |
|---|
| 1114 | + else |
|---|
| 1115 | + ret = -EFAULT; |
|---|
| 1116 | + break; |
|---|
| 1072 | 1117 | default: |
|---|
| 1073 | 1118 | ret = -ENOIOCTLCMD; |
|---|
| 1074 | 1119 | break; |
|---|
| .. | .. |
|---|
| 1080 | 1125 | |
|---|
| 1081 | 1126 | static int __sc230ai_start_stream(struct sc230ai *sc230ai) |
|---|
| 1082 | 1127 | { |
|---|
| 1083 | | - int ret; |
|---|
| 1128 | + int ret = 0; |
|---|
| 1084 | 1129 | |
|---|
| 1085 | 1130 | if (!sc230ai->is_thunderboot) { |
|---|
| 1086 | 1131 | ret = sc230ai_write_array(sc230ai->client, sc230ai->cur_mode->reg_list); |
|---|
| .. | .. |
|---|
| 1099 | 1144 | return ret; |
|---|
| 1100 | 1145 | } |
|---|
| 1101 | 1146 | } |
|---|
| 1147 | + if (sc230ai->sync_mode == INTERNAL_MASTER_MODE) |
|---|
| 1148 | + ret |= sc230ai_write_array(sc230ai->client, |
|---|
| 1149 | + sc230ai_interal_sync_master_start_regs); |
|---|
| 1150 | + else if (sc230ai->sync_mode == SLAVE_MODE) |
|---|
| 1151 | + ret |= sc230ai_write_array(sc230ai->client, |
|---|
| 1152 | + sc230ai_interal_sync_slaver_start_regs); |
|---|
| 1102 | 1153 | } |
|---|
| 1103 | | - return sc230ai_write_reg(sc230ai->client, SC230AI_REG_CTRL_MODE, |
|---|
| 1154 | + ret |= sc230ai_write_reg(sc230ai->client, SC230AI_REG_CTRL_MODE, |
|---|
| 1104 | 1155 | SC230AI_REG_VALUE_08BIT, SC230AI_MODE_STREAMING); |
|---|
| 1156 | + return ret; |
|---|
| 1105 | 1157 | } |
|---|
| 1106 | 1158 | |
|---|
| 1107 | 1159 | static int __sc230ai_stop_stream(struct sc230ai *sc230ai) |
|---|
| 1108 | 1160 | { |
|---|
| 1161 | + int ret = 0; |
|---|
| 1109 | 1162 | sc230ai->has_init_exp = false; |
|---|
| 1110 | 1163 | if (sc230ai->is_thunderboot) { |
|---|
| 1111 | 1164 | sc230ai->is_first_streamoff = true; |
|---|
| 1112 | 1165 | pm_runtime_put(&sc230ai->client->dev); |
|---|
| 1166 | + } else { |
|---|
| 1167 | + if (sc230ai->sync_mode == INTERNAL_MASTER_MODE) |
|---|
| 1168 | + ret |= sc230ai_write_array(sc230ai->client, |
|---|
| 1169 | + sc230ai_interal_sync_master_stop_regs); |
|---|
| 1170 | + else if (sc230ai->sync_mode == SLAVE_MODE) |
|---|
| 1171 | + ret |= sc230ai_write_array(sc230ai->client, |
|---|
| 1172 | + sc230ai_interal_sync_slaver_stop_regs); |
|---|
| 1113 | 1173 | } |
|---|
| 1114 | | - return sc230ai_write_reg(sc230ai->client, SC230AI_REG_CTRL_MODE, |
|---|
| 1174 | + ret |= sc230ai_write_reg(sc230ai->client, SC230AI_REG_CTRL_MODE, |
|---|
| 1115 | 1175 | SC230AI_REG_VALUE_08BIT, SC230AI_MODE_SW_STANDBY); |
|---|
| 1176 | + return ret; |
|---|
| 1116 | 1177 | } |
|---|
| 1117 | 1178 | |
|---|
| 1118 | 1179 | static int __sc230ai_power_on(struct sc230ai *sc230ai); |
|---|
| .. | .. |
|---|
| 1406 | 1467 | switch (ctrl->id) { |
|---|
| 1407 | 1468 | case V4L2_CID_VBLANK: |
|---|
| 1408 | 1469 | /* Update max exposure while meeting expected vblanking */ |
|---|
| 1409 | | - max = sc230ai->cur_mode->height + ctrl->val - 4; |
|---|
| 1470 | + max = sc230ai->cur_mode->height + ctrl->val - 5; |
|---|
| 1410 | 1471 | __v4l2_ctrl_modify_range(sc230ai->exposure, |
|---|
| 1411 | 1472 | sc230ai->exposure->minimum, max, |
|---|
| 1412 | 1473 | sc230ai->exposure->step, |
|---|
| .. | .. |
|---|
| 1469 | 1530 | (ctrl->val + sc230ai->cur_mode->height) |
|---|
| 1470 | 1531 | & 0xff); |
|---|
| 1471 | 1532 | sc230ai->cur_vts = ctrl->val + sc230ai->cur_mode->height; |
|---|
| 1472 | | - if (sc230ai->cur_vts != sc230ai->cur_mode->vts_def) |
|---|
| 1473 | | - sc230ai_modify_fps_info(sc230ai); |
|---|
| 1533 | + sc230ai_modify_fps_info(sc230ai); |
|---|
| 1474 | 1534 | break; |
|---|
| 1475 | 1535 | case V4L2_CID_TEST_PATTERN: |
|---|
| 1476 | 1536 | ret = sc230ai_enable_test_pattern(sc230ai, ctrl->val); |
|---|
| .. | .. |
|---|
| 1546 | 1606 | V4L2_CID_VBLANK, vblank_def, |
|---|
| 1547 | 1607 | SC230AI_VTS_MAX - mode->height, |
|---|
| 1548 | 1608 | 1, vblank_def); |
|---|
| 1549 | | - exposure_max = SC230AI_EXPOSURE_LIN_MAX; |
|---|
| 1609 | + exposure_max = mode->vts_def - 5; |
|---|
| 1550 | 1610 | sc230ai->exposure = v4l2_ctrl_new_std(handler, &sc230ai_ctrl_ops, |
|---|
| 1551 | 1611 | V4L2_CID_EXPOSURE, SC230AI_EXPOSURE_MIN, |
|---|
| 1552 | 1612 | exposure_max, SC230AI_EXPOSURE_STEP, |
|---|
| .. | .. |
|---|
| 1629 | 1689 | char facing[2]; |
|---|
| 1630 | 1690 | int ret; |
|---|
| 1631 | 1691 | u32 i, hdr_mode = 0; |
|---|
| 1692 | + const char *sync_mode_name = NULL; |
|---|
| 1632 | 1693 | |
|---|
| 1633 | 1694 | dev_info(dev, "driver version: %02x.%02x.%02x", |
|---|
| 1634 | 1695 | DRIVER_VERSION >> 16, |
|---|
| .. | .. |
|---|
| 1652 | 1713 | dev_err(dev, "could not get module information!\n"); |
|---|
| 1653 | 1714 | return -EINVAL; |
|---|
| 1654 | 1715 | } |
|---|
| 1716 | + |
|---|
| 1717 | + ret = of_property_read_string(node, RKMODULE_CAMERA_SYNC_MODE, |
|---|
| 1718 | + &sync_mode_name); |
|---|
| 1719 | + if (ret) { |
|---|
| 1720 | + sc230ai->sync_mode = NO_SYNC_MODE; |
|---|
| 1721 | + dev_err(dev, "could not get sync mode!\n"); |
|---|
| 1722 | + } else { |
|---|
| 1723 | + if (strcmp(sync_mode_name, RKMODULE_EXTERNAL_MASTER_MODE) == 0) { |
|---|
| 1724 | + sc230ai->sync_mode = EXTERNAL_MASTER_MODE; |
|---|
| 1725 | + dev_info(dev, "external master mode\n"); |
|---|
| 1726 | + } else if (strcmp(sync_mode_name, RKMODULE_INTERNAL_MASTER_MODE) == 0) { |
|---|
| 1727 | + sc230ai->sync_mode = INTERNAL_MASTER_MODE; |
|---|
| 1728 | + dev_info(dev, "internal master mode\n"); |
|---|
| 1729 | + } else if (strcmp(sync_mode_name, RKMODULE_SLAVE_MODE) == 0) { |
|---|
| 1730 | + sc230ai->sync_mode = SLAVE_MODE; |
|---|
| 1731 | + dev_info(dev, "slave mode\n"); |
|---|
| 1732 | + } |
|---|
| 1733 | + } |
|---|
| 1734 | + |
|---|
| 1655 | 1735 | sc230ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP); |
|---|
| 1656 | 1736 | sc230ai->client = client; |
|---|
| 1657 | 1737 | for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { |
|---|