diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-rockchip.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-rockchip.txt index 617572aa2d15..55865c276c2b 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-rockchip.txt +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-rockchip.txt @@ -8,83 +8,4 @@ property of OPPS when it is parsed by the OPP framework. This is based on operating-points-v2, but the driver can also create the "cpufreq-dt" platform_device to compatibility with operating-points. -For more information about the expected DT format [See: ../opp/opp.txt]. - -Optional properties: -In 'operating-points-v2' table: -- rockchip,leakage-voltage-sel: The property is an array of 3-tuples items, and - each item consists of leakage and voltage selector like - . - min-leakage: minimum leakage in mA, ranges from 1 to 254. - max-leakage: maximum leakage in mA, ranges from 1 to 254. - voltage-selector: a sequence number which is used to math - opp-microvolt-L roperty in OPP node. - -- rockchip,pvtm-voltage-sel: The property is an array of 3-tuples items, and - each item consists of pvtm and voltage selector like - . - min-pvtm: minimum frequency count in KHz. - max-pvtm: maximum frequency count in KHz. - voltage-selector: a sequence number which is used to math - opp-microvolt-L roperty in OPP node. -- rockchip,pvtm-freq: Clock frequency in KHz, which is used to set the cpu clock - frequency before get frequency count of pvtm. -- rockchip,pvtm-volt: Voltage in uV, which is used to set the cpu voltage before - get frequency count of pvtm. -- rockchip,pvtm-ch: An array of two integers containing pvtm channel and clock - oscillation ring. -- rockchip,pvtm-sample-time: The number of milliseconds to wait for pvtm to - finish counting. -- rockchip,pvtm-number: An integer indicating the number of sampling points. -- rockchip,pvtm-error: An integer indicating the error between the sample - results. -- rockchip,pvtm-ref-temp: The SoC internal temperature in degree centigrade, the - min-pvtm and max-pvtm in 'leakage-voltage-sel' are - measured at reference temperature. -- rockchip,pvtm-temp-prop: An array of two integers containing proportional - constants which is used to convert the value at current - temperature to reference temperature. The first one is - used when current temperature is below reference - temperature. Conversely, The second one is used when - current temperature is above reference temperature. -- rockchip,pvtm-thermal-zone: A thermal zone node containing thermal sensor, - it's used to get the current temperature. -- rockchip,thermal-zone: A thermal zone node containing thermal sensor, - it's used to get the current temperature. - -- nvmem-cells: A phandle to cpu_leakage data provided by a nvmem device. -- nvmem-cell-names: Should be "cpu_leakage" - -- rockchip,threshold-freq: Clock frequency in KHz, it's used to reduce power - for SoCs with two clusters. -- rockchip,freq-limit: Only one cluster can contain the property, and the - cluster's maximum frequency will be limited to its - threshold frequency, if the other cluster's frequency - is geater than or equal to its threshold frequency. - -Examples: - -cpus { - cpu@0 { - operating-points-v2 = <&cluster0_opp>; - }; -} - -cluster0_opp: opp_table0 { - compatible = "operating-points-v2"; - opp-shared; - rockchip,leakage-voltage-sel = < - 1 24 0 - 25 254 1 - >; - nvmem-cells = <&cpu_leakage>; - nvmem-cell-names = "cpu_leakage"; - - opp@216000000 { - opp-hz = /bits/ 64 <216000000>; - opp-microvolt = <950000 950000 1350000>; - opp-microvolt-L0 = <1050000 1050000 1350000>; - opp-microvolt-L1 = <950000 950000 1350000>; - opp-suspend; - } -}; +For more information about the expected DT format [See: ../opp/rockchip-opp.txt]. diff --git a/Documentation/devicetree/bindings/opp/rockchip-opp.txt b/Documentation/devicetree/bindings/opp/rockchip-opp.txt new file mode 100644 index 000000000000..b64f3dd1190c --- /dev/null +++ b/Documentation/devicetree/bindings/opp/rockchip-opp.txt @@ -0,0 +1,173 @@ +Rockchip OPP bindings to describe OPP nodes + +The bindings are based on top of the operating-points-v2 bindings +described in Documentation/devicetree/bindings/opp/opp.txt +Additional properties are described below. + +Optional properties: +In 'operating-points-v2' table: +- rockchip,supported-hw: The property allows to set supported hardware version. + +- rockchip,opp-shared-dsu: Indicates the dsu also uses the opp table. + +- rockchip,leakage-voltage-sel: The property is an array of 3-tuples items, and + each item consists of leakage and voltage selector like + . + min-leakage: minimum leakage in mA, ranges from 1 to 254. + max-leakage: maximum leakage in mA, ranges from 1 to 254. + voltage-selector: a sequence number which is used to math + opp-microvolt-L property in OPP node. +- rockchip,leakage-scaling-sel: Similar to 'rockchip,leakage-voltage-sel', this + allows to change max frequency according to leakage. + +- rockchip,pvtm-hw: Similar to 'opp-supported-hw', but only one hierarchical + level, if hardware version is set in the property, the + 'rockchip,pvtm-voltage-sel-hw' will be used, otherwise + 'rockchip,pvtm-voltage-sel' will be used. +- rockchip,pvtm-voltage-sel-hw: Similar to 'rockchip,pvtm-voltage-sel', this + depends on 'rockchip,pvtm-hw' property. +- rockchip,pvtm-voltage-sel: The property is an array of 3-tuples items, and + each item consists of pvtm and voltage selector like + . + min-pvtm: minimum frequency count in KHz. + max-pvtm: maximum frequency count in KHz. + voltage-selector: a sequence number which is used to math + opp-microvolt-L property in OPP node. +- rockchip,pvtm-scaling-sel: Similar to 'rockchip,pvtm-voltage-sel', this allows + to change maximum frequency according to pvtm. + +- rockchip,bin-voltage-sel: The property is an array of 2-tuples items, and + each item consists of bin and voltage selector like + . + min-bin: chip version. + voltage-selector: a sequence number which is used to math + opp-microvolt-L property in OPP node. +- rockchip,bin-scaling-sel: Similar to 'rockchip,bin-voltage-sel', this allows + to change maximum frequency according to chip version. + +- rockchip,grf: The phandle of the syscon node for GRF register. +- rockchip,dsu-grf: the phandle of the syscon node for DSU GRF register. +- rockchip,opp-clocks: List of clocks for accessing pvtpll and read margin. +- volt-mem-read-margin: The property is an array of 2-tuples items, and + each item consists voltage an memory read margin like + , this allows to change read margin + according to voltage. +- low-volt-mem-read-margin: The memory read margin value of low voltage. +- intermediate-threshold-freq: Clock frequency in KHz, if current frequency or + target frequency less than the threshold frequency, + there may be no need to set intermediate rate. + +- rockchip,pvtpll-avg-offset: The offset of average value register. +- rockchip,pvtpll-min-rate: Clock frequency in KHz, if opp frequency is higher + than the minimum frequency, the opp voltage will be + changed. +- rockchip,pvtpll-volt-step: Voltage step in uV. + +- rockchip,pvtm-pvtpll: Indicates pvtm value is from pvtpll. +- rockchip,pvtm-offset: The offset of pvtm value register. +- rockchip,pvtm-freq: Clock frequency in KHz, which is used to set the cpu clock + frequency before get frequency count of pvtm. +- rockchip,pvtm-volt: Voltage in uV, which is used to set the cpu voltage before + get frequency count of pvtm. +- rockchip,pvtm-ch: An array of two integers containing pvtm channel and clock + oscillation ring. +- rockchip,pvtm-sample-time: The number of milliseconds to wait for pvtm to + finish counting. +- rockchip,pvtm-number: An integer indicating the number of sampling points. +- rockchip,pvtm-error: An integer indicating the error between the sample + results. +- rockchip,pvtm-ref-temp: The SoC internal temperature in degree centigrade, the + min-pvtm and max-pvtm in 'leakage-voltage-sel' are + measured at reference temperature. +- rockchip,pvtm-temp-prop: An array of two integers containing proportional + constants which is used to convert the value at current + temperature to reference temperature. The first one is + used when current temperature is below reference + temperature. Conversely, The second one is used when + current temperature is above reference temperature. +- rockchip,pvtm-thermal-zone: A thermal zone node containing thermal sensor, + it's used to get the current temperature. +- rockchip,thermal-zone: A thermal zone node containing thermal sensor, + it's used to get the current temperature. + +- nvmem-cells: A phandle to soc info provided by a nvmem device. +- nvmem-cell-names: Should be "leakage", "pvtm", "mbist-vmin", "opp-info", + "specification_serial_number", "performance", or + "remark_spec_serial_number". + +- rockchip,threshold-freq: Clock frequency in KHz, it's used to reduce power + for SoCs with two clusters. +- rockchip,freq-limit: Only one cluster can contain the property, and the + cluster's maximum frequency will be limited to its + threshold frequency, if the other cluster's frequency + is geater than or equal to its threshold frequency. + +- rockchip,init-freq: The initial clock frequency in KHz. + +- rockchip,max-volt: The maximum voltage of opp. +- rockchip,evb-irdrop: The evb irdrop in uV. +- rockchip,board-irdrop: The property is an array of 3-tuples items, and + each item consists of frequency and voltage like + . This allows to change voltage + for different frequencice. + +- rockchip,temp-hysteresis: Temperature hysteresis in millicelsius. +- rockchip,low-temp: Threshold temperature in millicelsius. +- rockchip,low-temp-adjust-volt: The property is an array of 3-tuples items, and + each item consists of frequency and voltage like + . This allows to change voltage + according to different frequencice at low temperature. +- rockchip,low-temp-min-volt: The minimum voltage of regulator at low temperature. +- rockchip,high-temp: Threshold temperature in millicelsius. +- rockchip,high-temp-max-volt: The maximum voltage of regulator at high temperature. +- rockchip,high-temp-max-freq: The maximum frequency of clock at high temperature. +- rockchip,temp-freq-table: The property is an array of 2-tuples items, and each + item consists of temperature and frequency like + . This allows to change the max + frequency according to temperature. +- rockchip,high-temp-limit-table: Similar to 'rockchip,temp-freq-table', but + it's discarded. + +- rockchip,early-min-microvolt: Voltage in uV, this allows to limit the minimum + voltage of regulator when start up. + +- rockchip,video-4k-freq: Clock frequency in KHz, change to the frequency when + enter SYS_STATUS_VIDEO_4K status. +- rockchip,reboot-freq: Clock frequency in KHz, change to the frequency when + reboot. + +- rockchip,avs-enable: Similar to 'rockchip,avs', but it's discarded. +- rockchip,avs: Indicates avs is enabled. +- rockchip,avs-scale: The minimum scale of avs. +- clocks: PLL clock, it's used to get avs frequency and avs scale. + +- mbist-vmin: The minimum voltage of memory, this allows to adjust opp voltage + according to mbist in otp. + +Examples: + +cpus { + cpu@0 { + operating-points-v2 = <&cluster0_opp>; + }; +} + +cluster0_opp: opp_table0 { + compatible = "operating-points-v2"; + opp-shared; + + rockchip,leakage-voltage-sel = < + 1 24 0 + 25 254 1 + >; + nvmem-cells = <&cpu_leakage>; + nvmem-cell-names = "cpu_leakage"; + + opp@216000000 { + opp-hz = /bits/ 64 <216000000>; + opp-microvolt = <950000 950000 1350000>; + opp-microvolt-L0 = <1050000 1050000 1350000>; + opp-microvolt-L1 = <950000 950000 1350000>; + opp-suspend; + } +}; diff --git a/arch/arm/mach-rockchip/rv1106_pm.c b/arch/arm/mach-rockchip/rv1106_pm.c index 205c78d88507..ce0edb7743f5 100644 --- a/arch/arm/mach-rockchip/rv1106_pm.c +++ b/arch/arm/mach-rockchip/rv1106_pm.c @@ -389,7 +389,7 @@ static void rv1106_dbg_pmu_wkup_src(void) if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_USBDEV_EN)) rkpm_printstr("USBDEV detect wakeup\n"); - if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_TIMEROUT_EN)) + if (pmu_int_st & BIT(RV1106_PMU_WAKEUP_TIMEOUT_EN)) rkpm_printstr("TIMEOUT interrupt wakeup\n"); rkpm_printch('\n'); diff --git a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi index 14ab6a7eb403..8609dae175f6 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s.dtsi @@ -2414,8 +2414,7 @@ rockchip,pvtm-temp-prop = <(-135) (-135)>; rockchip,pvtm-thermal-zone = "gpu-thermal"; - clocks = <&cru CLK_GPU>; - clock-names = "clk"; + rockchip,opp-clocks = <&cru CLK_GPU>; rockchip,grf = <&gpu_grf>; volt-mem-read-margin = < 855000 1 @@ -3507,8 +3506,7 @@ rockchip,pvtm-temp-prop = <(-113) (-113)>; rockchip,pvtm-thermal-zone = "npu-thermal"; - clocks = <&cru PCLK_NPU_GRF>, <&cru HCLK_NPU_ROOT>; - clock-names = "pclk", "hclk"; + rockchip,opp-clocks = <&cru PCLK_NPU_GRF>, <&cru HCLK_NPU_ROOT>; rockchip,grf = <&npu_grf>; volt-mem-read-margin = < 855000 1 diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c index 7b5c7ee30fc2..e419bbb05ef3 100644 --- a/drivers/media/platform/rockchip/cif/capture.c +++ b/drivers/media/platform/rockchip/cif/capture.c @@ -676,7 +676,7 @@ static int rkcif_output_fmt_check(struct rkcif_stream *stream, break; } if (ret) - v4l2_err(&stream->cifdev->v4l2_dev, + v4l2_dbg(4, rkcif_debug, &stream->cifdev->v4l2_dev, "input mbus_code 0x%x, can't transform to %c%c%c%c\n", input_fmt->mbus_code, output_fmt->fourcc & 0xff, @@ -1933,6 +1933,26 @@ static void rkcif_assign_new_buffer_init_toisp(struct rkcif_stream *stream, stream->buf_owner = RKCIF_DMAEN_BY_ISP; } +static void rkcif_dphy_quick_stream(struct rkcif_device *dev, int on) +{ + struct rkcif_pipeline *p = NULL; + int j = 0; + + if (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY || + dev->active_sensor->mbus.type == V4L2_MBUS_CCP2) { + p = &dev->pipe; + for (j = 0; j < p->num_subdevs; j++) { + if (p->subdevs[j] != dev->terminal_sensor.sd && + p->subdevs[j] != dev->active_sensor->sd) { + v4l2_subdev_call(p->subdevs[j], core, ioctl, + RKMODULE_SET_QUICK_STREAM, &on); + break; + } + } + } +} + static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream, int channel_id) { @@ -1944,6 +1964,7 @@ static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream, struct sditf_priv *priv = dev->sditf[0]; u32 frm_addr_y, buff_addr_y; unsigned long flags; + int on = 0; if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || @@ -2024,17 +2045,6 @@ static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream, } if (stream->lack_buf_cnt) stream->lack_buf_cnt--; - if (stream->lack_buf_cnt == 2 || stream->is_single_cap) { - stream->to_stop_dma = RKCIF_DMAEN_BY_ISP; - rkcif_stop_dma_capture(stream); - stream->is_single_cap = false; - if ((dev->hdr.hdr_mode == NO_HDR && atomic_read(&dev->streamoff_cnt) == 1) || - (dev->hdr.hdr_mode == HDR_X2 && atomic_read(&dev->streamoff_cnt) == 2) || - (dev->hdr.hdr_mode == HDR_X3 && atomic_read(&dev->streamoff_cnt) == 3)) { - dev->sensor_work.on = 0; - schedule_work(&dev->sensor_work.work); - } - } } else { if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE) goto out_get_buf; @@ -2107,7 +2117,18 @@ static int rkcif_assign_new_buffer_update_toisp(struct rkcif_stream *stream, if (dev->is_support_tools && stream->tools_vdev && active_buf) rkcif_rdbk_with_tools(stream, active_buf); } - + if (stream->is_single_cap) { + stream->to_stop_dma = RKCIF_DMAEN_BY_ISP; + rkcif_stop_dma_capture(stream); + rkcif_dphy_quick_stream(stream->cifdev, on); + stream->is_single_cap = false; + if ((dev->hdr.hdr_mode == NO_HDR && atomic_read(&dev->streamoff_cnt) == 1) || + (dev->hdr.hdr_mode == HDR_X2 && atomic_read(&dev->streamoff_cnt) == 2) || + (dev->hdr.hdr_mode == HDR_X3 && atomic_read(&dev->streamoff_cnt) == 3)) { + dev->sensor_work.on = 0; + schedule_work(&dev->sensor_work.work); + } + } out_get_buf: stream->frame_phase_cache = stream->frame_phase; if (buffer) { @@ -2531,6 +2552,7 @@ static int rkcif_assign_new_buffer_update(struct rkcif_stream *stream, int ret = 0; u32 buff_addr_y, buff_addr_cbcr; unsigned long flags; + int on = 0; if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY || mbus_cfg->type == V4L2_MBUS_CSI2_CPHY || @@ -2629,6 +2651,18 @@ static int rkcif_assign_new_buffer_update(struct rkcif_stream *stream, } } stream->frame_phase_cache = stream->frame_phase; + if (stream->is_single_cap) { + stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP; + rkcif_stop_dma_capture(stream); + rkcif_dphy_quick_stream(stream->cifdev, on); + stream->is_single_cap = false; + if ((dev->hdr.hdr_mode == NO_HDR && atomic_read(&dev->streamoff_cnt) == 1) || + (dev->hdr.hdr_mode == HDR_X2 && atomic_read(&dev->streamoff_cnt) == 2) || + (dev->hdr.hdr_mode == HDR_X3 && atomic_read(&dev->streamoff_cnt) == 3)) { + dev->sensor_work.on = on; + schedule_work(&dev->sensor_work.work); + } + } if (buffer) { buff_addr_y = buffer->buff_addr[RKCIF_PLANE_Y]; @@ -6610,6 +6644,8 @@ void rkcif_stream_init(struct rkcif_device *dev, u32 id) stream->is_single_cap = false; atomic_set(&stream->buf_cnt, 0); stream->rx_buf_num = 0; + init_completion(&stream->stop_complete); + stream->is_wait_stop_complete = false; } static int rkcif_fh_open(struct file *filp) @@ -7209,10 +7245,12 @@ static long rkcif_ioctl_default(struct file *file, void *fh, struct csi_channel_info csi_info; struct rkcif_fps fps; int reset_src; - int on = 0; + struct rkcif_quick_stream_param *stream_param; bool is_single_dev = false; struct v4l2_subdev *sd; int ret = -EINVAL; + int i = 0; + int stream_num = 0; switch (cmd) { case RKCIF_CMD_GET_CSI_MEMORY_MODE: @@ -7262,12 +7300,22 @@ static long rkcif_ioctl_default(struct file *file, void *fh, reset_src = *(int *)arg; return rkcif_do_reset_work(dev, reset_src); case RKCIF_CMD_SET_QUICK_STREAM: - on = *(int *)arg; + stream_param = (struct rkcif_quick_stream_param *)arg; if (!dev->sditf[0]) return -EINVAL; - if (on) { + if (dev->hdr.hdr_mode == HDR_X2) + stream_num = 2; + else if (dev->hdr.hdr_mode == HDR_X3) + stream_num = 3; + else + stream_num = 1; + if (stream_param->on) { is_single_dev = rkcif_check_single_dev_stream_on(dev->hw_dev); if (is_single_dev) { + for (i = 0; i < stream_num - 1; i++) { + dev->stream[i].to_en_dma = RKCIF_DMAEN_BY_ISP; + rkcif_enable_dma_capture(&dev->stream[i], true); + } dev->sditf[0]->mode.rdbk_mode = RKISP_VICAP_ONLINE; sditf_change_to_online(dev->sditf[0]); sd = get_rkisp_sd(dev->sditf[0]); @@ -7279,23 +7327,47 @@ static long rkcif_ioctl_default(struct file *file, void *fh, return -EINVAL; } } else { - dev->sditf[0]->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO; sditf_disable_immediately(dev->sditf[0]); - stream->to_en_dma = RKCIF_DMAEN_BY_ISP; - rkcif_enable_dma_capture(stream, true); + for (i = 0; i < stream_num; i++) { + if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) + dev->stream[i].to_en_dma = RKCIF_DMAEN_BY_ISP; + else + dev->stream[i].to_en_dma = RKCIF_DMAEN_BY_VICAP; + rkcif_enable_dma_capture(&dev->stream[i], true); + } } - + rkcif_dphy_quick_stream(dev, stream_param->on); v4l2_subdev_call(dev->terminal_sensor.sd, core, ioctl, - RKMODULE_SET_QUICK_STREAM, &on); + RKMODULE_SET_QUICK_STREAM, &stream_param->on); } else { - if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) { - stream->to_stop_dma = RKCIF_DMAEN_BY_ISP; - } else if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AIQ) { - stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP; - } else { - stream->cifdev->sensor_state = 0; + if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_ONLINE) { + for (i = 0; i < stream_num - 1; i++) { + reinit_completion(&dev->stream[i].stop_complete); + dev->stream[i].is_wait_stop_complete = true; + dev->stream[i].to_stop_dma = RKCIF_DMAEN_BY_ISP; + wait_for_completion_timeout(&dev->stream[i].stop_complete, + msecs_to_jiffies(RKCIF_STOP_MAX_WAIT_TIME_MS)); + } + stream->cifdev->sensor_state = stream_param->on; stream->cifdev->sensor_state_change = true; + dev->stream[i].is_wait_stop_complete = true; + wait_for_completion_timeout(&dev->stream[i].stop_complete, + msecs_to_jiffies(RKCIF_STOP_MAX_WAIT_TIME_MS)); + } else { + for (i = 0; i < stream_num; i++) { + dev->stream[i].is_wait_stop_complete = true; + reinit_completion(&dev->stream[i].stop_complete); + if (dev->sditf[0]->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) + dev->stream[i].to_stop_dma = RKCIF_DMAEN_BY_ISP; + else + dev->stream[i].to_stop_dma = RKCIF_DMAEN_BY_VICAP; + wait_for_completion_timeout(&dev->stream[i].stop_complete, + msecs_to_jiffies(RKCIF_STOP_MAX_WAIT_TIME_MS)); + } } + stream_param->frame_num = dev->stream[0].frame_idx - 1; + if (!dev->is_rtt_suspend) + dev->resume_mode = stream_param->resume_mode; } break; default: @@ -9994,6 +10066,7 @@ static void rkcif_toisp_check_stop_status(struct sditf_priv *priv, int i = 0; u32 val = 0; u64 cur_time = 0; + int on = 0; for (i = 0; i < TOISP_CH_MAX; i++) { ch = rkcif_g_toisp_ch(intstat_glb, index); @@ -10012,9 +10085,20 @@ static void rkcif_toisp_check_stop_status(struct sditf_priv *priv, wake_up(&stream->wq_stopped); } if (stream->cifdev->sensor_state_change) { + rkcif_dphy_quick_stream(stream->cifdev, on); stream->cifdev->sensor_work.on = stream->cifdev->sensor_state; schedule_work(&stream->cifdev->sensor_work.work); stream->cifdev->sensor_state_change = false; + if (stream->is_wait_stop_complete) { + stream->is_wait_stop_complete = false; + complete(&stream->stop_complete); + } + } + if (stream->is_single_cap) { + rkcif_dphy_quick_stream(stream->cifdev, on); + stream->cifdev->sensor_work.on = 0; + schedule_work(&stream->cifdev->sensor_work.work); + stream->is_single_cap = false; } if (stream->cifdev->chip_id >= CHIP_RV1106_CIF) rkcif_modify_frame_skip_config(stream); @@ -10329,7 +10413,6 @@ static void rkcif_get_resmem_head(struct rkcif_device *cif_dev) int cam_idx = 0; char cam_idx_str[3] = {0}; - cif_dev->resume_mode = RKISP_RTT_MODE_NORMAL; if (!cif_dev->is_thunderboot && !cif_dev->is_rtt_suspend) return; strscpy(cam_idx_str, cif_dev->terminal_sensor.sd->name + 1, 2); @@ -10403,7 +10486,7 @@ static int rkcif_subdevs_set_stream(struct rkcif_device *cif_dev, int on) for (i = 0; i < p->num_subdevs; i++) { if (p->subdevs[i] == terminal_sensor->sd && on) rkcif_set_sof(cif_dev, cif_dev->stream[0].frame_idx); - if (p->subdevs[i] == terminal_sensor->sd && cif_dev->is_rtt_suspend) { + if (p->subdevs[i] == terminal_sensor->sd) { if (!rk_tb_mcu_is_done() && on) { cif_dev->tb_client.data = p->subdevs[i]; cif_dev->tb_client.cb = rkcif_sensor_quick_streaming_cb; @@ -10517,6 +10600,7 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode) { struct rkcif_stream *stream = NULL; struct sditf_priv *priv = cif_dev->sditf[0]; + struct v4l2_subdev *sd = NULL; int ret = 0; int i = 0; u32 capture_mode = 0; @@ -10530,9 +10614,26 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode) rkcif_get_resmem_head(cif_dev); is_single_dev = rkcif_check_single_dev_stream_on(cif_dev->hw_dev); if (cif_dev->resume_mode == RKISP_RTT_MODE_ONE_FRAME) { - capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK; - if (priv) - priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO; + if (cif_dev->is_rtt_suspend) { + capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK; + if (priv) + priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO; + } else { + if (priv && priv->mode.rdbk_mode == RKISP_VICAP_ONLINE) { + if (cif_dev->chip_id == CHIP_RV1106_CIF) { + capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK; + priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO; + } else { + capture_mode = RKCIF_STREAM_MODE_TOISP; + } + } else if (priv && + (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO || + priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO_ONE_FRAME)) { + capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK; + } else { + capture_mode = RKCIF_STREAM_MODE_CAPTURE; + } + } } else if (cif_dev->resume_mode == RKISP_RTT_MODE_MULTI_FRAME) { if (is_single_dev) { capture_mode = RKCIF_STREAM_MODE_TOISP; @@ -10546,7 +10647,9 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode) } else { if (priv && priv->mode.rdbk_mode == RKISP_VICAP_ONLINE) capture_mode = RKCIF_STREAM_MODE_TOISP; - else if (priv && priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) + else if (priv && + (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO || + priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO_ONE_FRAME)) capture_mode = RKCIF_STREAM_MODE_TOISP_RDBK; else capture_mode = RKCIF_STREAM_MODE_CAPTURE; @@ -10621,6 +10724,18 @@ int rkcif_stream_resume(struct rkcif_device *cif_dev, int mode) (priv->hdr_cfg.hdr_mode == HDR_X3 && (stream->id == 0 || stream->id == 1)))) rkcif_init_rx_buf(stream, 1); } else { + if (stream->is_single_cap && stream->id == 0) { + priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AUTO_ONE_FRAME; + sd = get_rkisp_sd(priv); + if (sd) { + ret = v4l2_subdev_call(sd, core, ioctl, + RKISP_VICAP_CMD_MODE, &priv->mode); + if (ret) + v4l2_err(&cif_dev->v4l2_dev, + "set isp work mode rdbk aotu oneframe fail\n"); + + } + } sditf_disable_immediately(priv); if (!stream->rx_buf_num && capture_mode == RKCIF_STREAM_MODE_TOISP_RDBK) { @@ -10884,6 +10999,13 @@ void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev) stream->is_finish_stop_dma = false; continue; } + + if (stream->is_finish_stop_dma && stream->is_wait_stop_complete) { + stream->is_finish_stop_dma = false; + stream->is_wait_stop_complete = false; + complete(&stream->stop_complete); + } + if (stream->crop_dyn_en) rkcif_dynamic_crop(stream); @@ -10922,12 +11044,13 @@ void rkcif_irq_pingpong_v1(struct rkcif_device *cif_dev) } spin_lock_irqsave(&stream->vbq_lock, flags); - if (!(stream->cur_stream_mode & RKCIF_STREAM_MODE_TOISP) && stream->lack_buf_cnt == 2) { + if (!(stream->cur_stream_mode & RKCIF_STREAM_MODE_TOISP) && + stream->lack_buf_cnt == 2) { + stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP; + rkcif_stop_dma_capture(stream); v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev, "stream[%d] to stop dma, lack_buf_cnt %d\n", stream->id, stream->lack_buf_cnt); - stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP; - rkcif_stop_dma_capture(stream); } spin_unlock_irqrestore(&stream->vbq_lock, flags); if (stream->to_en_scale) { diff --git a/drivers/media/platform/rockchip/cif/dev.c b/drivers/media/platform/rockchip/cif/dev.c index 17d4e60de71b..bd5a4e36b69b 100644 --- a/drivers/media/platform/rockchip/cif/dev.c +++ b/drivers/media/platform/rockchip/cif/dev.c @@ -2216,7 +2216,7 @@ static int rkcif_plat_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused rkcif_runtime_prepare(struct device *dev) +static int __maybe_unused rkcif_sleep_suspend(struct device *dev) { struct rkcif_device *cif_dev = dev_get_drvdata(dev); @@ -2224,11 +2224,12 @@ static int __maybe_unused rkcif_runtime_prepare(struct device *dev) return 0; } -static void __maybe_unused rkcif_runtime_complete(struct device *dev) +static int __maybe_unused rkcif_sleep_resume(struct device *dev) { struct rkcif_device *cif_dev = dev_get_drvdata(dev); rkcif_stream_resume(cif_dev, RKCIF_RESUME_CIF); + return 0; } static int __maybe_unused rkcif_runtime_suspend(struct device *dev) @@ -2301,8 +2302,7 @@ late_initcall(rkcif_clr_unready_dev); #endif static const struct dev_pm_ops rkcif_plat_pm_ops = { - .prepare = rkcif_runtime_prepare, - .complete = rkcif_runtime_complete, + SET_SYSTEM_SLEEP_PM_OPS(rkcif_sleep_suspend, rkcif_sleep_resume) SET_RUNTIME_PM_OPS(rkcif_runtime_suspend, rkcif_runtime_resume, NULL) }; diff --git a/drivers/media/platform/rockchip/cif/dev.h b/drivers/media/platform/rockchip/cif/dev.h index 9fbd5bb7b6ef..a33277ea19b1 100644 --- a/drivers/media/platform/rockchip/cif/dev.h +++ b/drivers/media/platform/rockchip/cif/dev.h @@ -94,6 +94,11 @@ #define CROP_SRC_SENSOR_MASK (0x1 << 0) #define CROP_SRC_USR_MASK (0x1 << 1) +/* + * max wait time for stream stop + */ +#define RKCIF_STOP_MAX_WAIT_TIME_MS (500) + enum rkcif_workmode { RKCIF_WORKMODE_ONEFRAME = 0x00, RKCIF_WORKMODE_PINGPONG = 0x01, @@ -541,6 +546,7 @@ struct rkcif_stream { int last_frame_idx; int new_fource_idx; atomic_t buf_cnt; + struct completion stop_complete; bool stopping; bool crop_enable; bool crop_dyn_en; @@ -559,6 +565,7 @@ struct rkcif_stream { bool is_stop_capture; bool is_wait_dma_stop; bool is_single_cap; + bool is_wait_stop_complete; }; struct rkcif_lvds_subdev { diff --git a/drivers/media/platform/rockchip/cif/subdev-itf.c b/drivers/media/platform/rockchip/cif/subdev-itf.c index a0ad4026f81d..f67b46729723 100644 --- a/drivers/media/platform/rockchip/cif/subdev-itf.c +++ b/drivers/media/platform/rockchip/cif/subdev-itf.c @@ -494,9 +494,9 @@ static int sditf_channel_enable(struct sditf_priv *priv, int user) ch0 = 24;//dvp ctrl_val = (ch0 << 3) | 0x1; if (user == 0) - int_en = CIF_TOISP0_FS(0); + int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FE(0); else - int_en = CIF_TOISP1_FS(0); + int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FE(0); priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; } else if (priv->hdr_cfg.hdr_mode == HDR_X2) { @@ -505,9 +505,11 @@ static int sditf_channel_enable(struct sditf_priv *priv, int user) ctrl_val = (ch0 << 3) | 0x1; ctrl_val |= (ch1 << 11) | 0x100; if (user == 0) - int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1); + int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | + CIF_TOISP0_FE(0) | CIF_TOISP0_FE(1); else - int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1); + int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | + CIF_TOISP1_FE(0) | CIF_TOISP1_FE(1); priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; priv->toisp_inf.ch_info[1].is_valid = true; @@ -520,9 +522,11 @@ static int sditf_channel_enable(struct sditf_priv *priv, int user) ctrl_val |= (ch1 << 11) | 0x100; ctrl_val |= (ch2 << 19) | 0x10000; if (user == 0) - int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | CIF_TOISP0_FS(2); + int_en = CIF_TOISP0_FS(0) | CIF_TOISP0_FS(1) | CIF_TOISP0_FS(2) | + CIF_TOISP0_FE(0) | CIF_TOISP0_FE(1) | CIF_TOISP0_FE(2); else - int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | CIF_TOISP1_FS(2); + int_en = CIF_TOISP1_FS(0) | CIF_TOISP1_FS(1) | CIF_TOISP1_FS(2) | + CIF_TOISP1_FE(0) | CIF_TOISP1_FE(1) | CIF_TOISP1_FE(2); priv->toisp_inf.ch_info[0].is_valid = true; priv->toisp_inf.ch_info[0].id = ch0; priv->toisp_inf.ch_info[1].is_valid = true; @@ -853,7 +857,7 @@ static int sditf_s_rx_buffer(struct v4l2_subdev *sd, return -EINVAL; rx_buf = to_cif_rx_buf(dbufs); - v4l2_dbg(rkcif_debug, 3, &cif_dev->v4l2_dev, "buf back to vicap 0x%x\n", + v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev, "buf back to vicap 0x%x\n", (u32)rx_buf->dummy.dma_addr); spin_lock_irqsave(&stream->vbq_lock, flags); stream->last_rx_buf_idx = dbufs->sequence + 1; diff --git a/drivers/mfd/display-serdes/Makefile b/drivers/mfd/display-serdes/Makefile index 420076ccc22e..f5a5a15642c8 100644 --- a/drivers/mfd/display-serdes/Makefile +++ b/drivers/mfd/display-serdes/Makefile @@ -10,4 +10,4 @@ obj-$(CONFIG_SERDES_DISPLAY_CHIP_NOVO) += novo/ serdes-mfd-display-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-core.o serdes-irq.o -obj-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-mfd-display.o serdes-i2c.o serdes-bridge.o serdes-panel.o serdes-gpio.o serdes-pinctrl.o +obj-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-mfd-display.o serdes-i2c.o serdes-bridge.o serdes-bridge-split.o serdes-panel.o serdes-panel-split.o serdes-gpio.o serdes-pinctrl.o diff --git a/drivers/mfd/display-serdes/core.h b/drivers/mfd/display-serdes/core.h index 22b962b35f2a..8563ae5e7fac 100644 --- a/drivers/mfd/display-serdes/core.h +++ b/drivers/mfd/display-serdes/core.h @@ -102,8 +102,15 @@ #endif #define MFD_SERDES_DISPLAY_VERSION "serdes-mfd-displaly-v10-230901" - +#define MAX_NUM_SERDES_SPLIT 8 struct serdes; +enum ser_link_mode { + SER_DUAL_LINK, + SER_LINKA, + SER_LINKB, + SER_SPLITTER_MODE, +}; + struct serdes_chip_pinctrl_info { struct pinctrl_pin_desc *pins; unsigned int num_pins; @@ -163,6 +170,12 @@ struct serdes_chip_gpio_ops { int (*to_irq)(struct serdes *serdes, int gpio); }; +struct serdes_chip_split_ops { + int (*select)(struct serdes *serdes, int chan); + int (*deselect)(struct serdes *serdes, int chan); + int (*set_i2c_addr)(struct serdes *serdes, int address, int link); +}; + struct serdes_chip_pm_ops { /* serdes chip function for suspend and resume */ int (*suspend)(struct serdes *serdes); @@ -198,6 +211,7 @@ struct serdes_chip_data { struct serdes_chip_panel_ops *panel_ops; struct serdes_chip_pinctrl_ops *pinctrl_ops; struct serdes_chip_gpio_ops *gpio_ops; + struct serdes_chip_split_ops *split_ops; struct serdes_chip_pm_ops *pm_ops; struct serdes_chip_irq_ops *irq_ops; }; @@ -241,7 +255,35 @@ struct serdes_panel { struct serdes *parent; struct regmap *regmap; struct mipi_dsi_device *dsi; - struct device_node *dsi_node; + struct device_node *remote_node; + struct drm_display_mode mode; + struct backlight_device *backlight; + struct serdes_init_seq *serdes_init_seq; + bool sel_mipi; + bool dv_swp_ab; + bool dpi_deskew_en; + bool split_mode; + u32 num_lanes; + u32 dsi_lane_map[4]; +}; + +struct serdes_panel_split { + struct drm_panel panel; + enum drm_connector_status status; + struct drm_connector connector; + + const char *name; + u32 width_mm; + u32 height_mm; + u32 link_rate; + u32 lane_count; + bool ssc; + + struct device *dev; + struct serdes *parent; + struct regmap *regmap; + struct mipi_dsi_device *dsi; + struct device_node *remote_node; struct drm_display_mode mode; struct backlight_device *backlight; struct serdes_init_seq *serdes_init_seq; @@ -265,7 +307,7 @@ struct serdes_bridge { struct serdes *parent; struct regmap *regmap; struct mipi_dsi_device *dsi; - struct device_node *dsi_node; + struct device_node *remote_node; struct drm_display_mode mode; struct backlight_device *backlight; @@ -277,6 +319,29 @@ struct serdes_bridge { u32 dsi_lane_map[4]; }; +struct serdes_bridge_split { + struct drm_bridge base_bridge; + struct drm_bridge *next_bridge; + enum drm_connector_status status; + atomic_t triggered; + struct drm_connector connector; + struct drm_panel *panel; + + struct device *dev; + struct serdes *parent; + struct regmap *regmap; + struct mipi_dsi_device *dsi; + struct device_node *remote_node; + struct drm_display_mode mode; + struct backlight_device *backlight; + + bool sel_mipi; + bool dv_swp_ab; + bool dpi_deskew_en; + u32 num_lanes; + u32 dsi_lane_map[4]; +}; + struct serdes { int num_gpio; struct mutex io_lock; @@ -307,6 +372,15 @@ struct serdes { struct delayed_work mfd_delay_work; bool route_enable; bool use_delay_work; + + bool split_mode_enable; + unsigned int reg_hw; + unsigned int reg_use; + unsigned int link_use; + unsigned int id_serdes_bridge_split; + unsigned int id_serdes_panel_split; + struct serdes *g_serdes_bridge_split; + struct pinctrl *pinctrl_node; struct pinctrl_state *pins_default; struct pinctrl_state *pins_init; @@ -314,7 +388,9 @@ struct serdes { struct serdes_init_seq *serdes_init_seq; struct serdes_bridge *serdes_bridge; + struct serdes_bridge_split *serdes_bridge_split; struct serdes_panel *serdes_panel; + struct serdes_panel_split *serdes_panel_split; struct serdes_pinctrl *pinctrl; struct serdes_chip_data *chip_data; }; diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96745.c b/drivers/mfd/display-serdes/maxim/maxim-max96745.c index 3c83a8be9968..ad0d43ffd89f 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96745.c +++ b/drivers/mfd/display-serdes/maxim/maxim-max96745.c @@ -762,6 +762,84 @@ static struct serdes_chip_gpio_ops max96745_gpio_ops = { .to_irq = max96745_gpio_to_irq, }; +static int max96745_select(struct serdes *serdes, int chan) +{ + /*0076 for linkA and 0086 for linkB*/ + if (chan == DUAL_LINK) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + SERDES_DBG_CHIP("%s: enable %s remote i2c of linkA and linkB\n", __func__, + serdes->chip_data->name); + } else if (chan == LINKA) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + SERDES_DBG_CHIP("%s: only enable %s remote i2c of linkA\n", __func__, + serdes->chip_data->name); + } else if (chan == LINKB) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + SERDES_DBG_CHIP("%s: only enable %s remote i2c of linkB\n", __func__, + serdes->chip_data->name); + } else if (chan == SPLITTER_MODE) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + SERDES_DBG_CHIP("%s: enable %s remote i2c of linkA and linkB\n", __func__, + serdes->chip_data->name); + } + + return 0; +} + +static int max96745_deselect(struct serdes *serdes, int chan) +{ + + if (chan == DUAL_LINK) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + SERDES_DBG_CHIP("%s: disable %s remote i2c of linkA and linkB\n", __func__, + serdes->chip_data->name); + } else if (chan == LINKA) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + SERDES_DBG_CHIP("%s: only disable %s remote i2c of linkA\n", __func__, + serdes->chip_data->name); + } else if (chan == LINKB) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + SERDES_DBG_CHIP("%s: only disable %s remote i2c of linkB\n", __func__, + serdes->chip_data->name); + } else if (chan == SPLITTER_MODE) { + serdes_set_bits(serdes, 0x0076, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + serdes_set_bits(serdes, 0x0086, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 1)); + SERDES_DBG_CHIP("%s: disable %s remote i2c of linkA and linkB\n", __func__, + serdes->chip_data->name); + } + + return 0; +} + + +static struct serdes_chip_split_ops max96745_split_ops = { + .select = max96745_select, + .deselect = max96745_deselect, +}; + static int max96745_pm_suspend(struct serdes *serdes) { return 0; @@ -802,6 +880,7 @@ struct serdes_chip_data serdes_max96745_data = { .bridge_ops = &max96745_bridge_ops, .pinctrl_ops = &max96745_pinctrl_ops, .gpio_ops = &max96745_gpio_ops, + .split_ops = &max96745_split_ops, .pm_ops = &max96745_pm_ops, .irq_ops = &max96745_irq_ops, }; diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96745.h b/drivers/mfd/display-serdes/maxim/maxim-max96745.h index 031f4f901596..5d17fe50c05c 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96745.h +++ b/drivers/mfd/display-serdes/maxim/maxim-max96745.h @@ -136,4 +136,11 @@ /* 7074h */ #define MAX_LINK_RATE GENMASK(7, 0) +enum link_mode { + DUAL_LINK, + LINKA, + LINKB, + SPLITTER_MODE, +}; + #endif diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96752.c b/drivers/mfd/display-serdes/maxim/maxim-max96752.c index 49515098946a..5b13ce2b7cdf 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96752.c +++ b/drivers/mfd/display-serdes/maxim/maxim-max96752.c @@ -567,6 +567,37 @@ static struct serdes_chip_gpio_ops max96752_gpio_ops = { .to_irq = max96752_gpio_to_irq, }; +static int max96752_set_i2c_addr(struct serdes *serdes, int address, int link) +{ + int ret; + + if (link == LINKA) { + /* TX_SRC_ID[1] = 0 */ + ret = serdes_reg_write(serdes, 0x73, 0x31); + /* Receive packets with this stream ID = 0 */ + ret = serdes_reg_write(serdes, 0x50, 0x00); + ret = serdes_reg_write(serdes, 0x00, address << 1); + } else if (link == LINKB) { + /* TX_SRC_ID[1] = 1 */ + ret = serdes_reg_write(serdes, 0x73, 0x32); + /* Receive packets with this stream ID = 1 */ + ret = serdes_reg_write(serdes, 0x50, 0x01); + ret = serdes_reg_write(serdes, 0x00, address << 1); + } else { + dev_info(serdes->dev, "link %d is error\n", link); + ret = -1; + } + + SERDES_DBG_CHIP("%s: set serdes chip %s i2c 7bit address to 0x%x\n", __func__, + serdes->chip_data->name, address); + + return ret; +} + +static struct serdes_chip_split_ops max96752_split_ops = { + .set_i2c_addr = max96752_set_i2c_addr, +}; + static int max96752_pm_suspend(struct serdes *serdes) { return 0; @@ -607,6 +638,7 @@ struct serdes_chip_data serdes_max96752_data = { .panel_ops = &max96752_panel_ops, .bridge_ops = &max96752_bridge_ops, .pinctrl_ops = &max96752_pinctrl_ops, + .split_ops = &max96752_split_ops, .gpio_ops = &max96752_gpio_ops, .pm_ops = &max96752_pm_ops, .irq_ops = &max96752_irq_ops, diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96752.h b/drivers/mfd/display-serdes/maxim/maxim-max96752.h index 7e3e4aa551c3..dc1e960e350f 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96752.h +++ b/drivers/mfd/display-serdes/maxim/maxim-max96752.h @@ -35,4 +35,11 @@ #define OVR_RES_CFG BIT(7) #define GPIO_RX_ID GENMASK(4, 0) +enum link_mode { + DUAL_LINK, + LINKA, + LINKB, + SPLITTER_MODE, +}; + #endif diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96789.c b/drivers/mfd/display-serdes/maxim/maxim-max96789.c index b970f10ed786..50791118cac6 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96789.c +++ b/drivers/mfd/display-serdes/maxim/maxim-max96789.c @@ -679,6 +679,89 @@ static struct serdes_chip_gpio_ops max96789_gpio_ops = { .to_irq = max96789_gpio_to_irq, }; +static int max96789_select(struct serdes *serdes, int chan) +{ + u32 link_cfg, val; + int ret; + + serdes_set_bits(serdes, 0x0001, DIS_REM_CC, + FIELD_PREP(DIS_REM_CC, 0)); + + serdes_reg_read(serdes, 0x0010, &link_cfg); + if ((link_cfg & LINK_CFG) == SPLITTER_MODE) + SERDES_DBG_CHIP("%s: serdes chip %s already split mode cfg=0x%x\n", __func__, + serdes->chip_data->name, link_cfg); + + if (chan == 0 && (link_cfg & LINK_CFG) != DUAL_LINK) { + serdes_set_bits(serdes, 0x0004, + LINK_EN_B | LINK_EN_A, + FIELD_PREP(LINK_EN_A, 1) | + FIELD_PREP(LINK_EN_B, 1)); + serdes_set_bits(serdes, 0x0010, + RESET_ONESHOT | AUTO_LINK | LINK_CFG, + FIELD_PREP(RESET_ONESHOT, 1) | + FIELD_PREP(AUTO_LINK, 0) | + FIELD_PREP(LINK_CFG, DUAL_LINK)); + SERDES_DBG_CHIP("%s: change to use dual link\n", __func__); + } else if (chan == 1 && (link_cfg & LINK_CFG) != LINKA) { + serdes_set_bits(serdes, 0x0004, + LINK_EN_B | LINK_EN_A, + FIELD_PREP(LINK_EN_A, 1) | + FIELD_PREP(LINK_EN_B, 0)); + serdes_set_bits(serdes, 0x0010, + RESET_ONESHOT | AUTO_LINK | LINK_CFG, + FIELD_PREP(RESET_ONESHOT, 1) | + FIELD_PREP(AUTO_LINK, 0) | + FIELD_PREP(LINK_CFG, LINKA)); + SERDES_DBG_CHIP("%s: change to use linkA\n", __func__); + } else if (chan == 2 && (link_cfg & LINK_CFG) != LINKB) { + serdes_set_bits(serdes, 0x0004, + LINK_EN_B | LINK_EN_A, + FIELD_PREP(LINK_EN_A, 0) | + FIELD_PREP(LINK_EN_B, 1)); + serdes_set_bits(serdes, 0x0010, + RESET_ONESHOT | AUTO_LINK | LINK_CFG, + FIELD_PREP(RESET_ONESHOT, 1) | + FIELD_PREP(AUTO_LINK, 0) | + FIELD_PREP(LINK_CFG, LINKB)); + SERDES_DBG_CHIP("%s: change to use linkB\n", __func__); + } else if (chan == 3 && (link_cfg & LINK_CFG) != SPLITTER_MODE) { + serdes_set_bits(serdes, 0x0004, + LINK_EN_B | LINK_EN_A, + FIELD_PREP(LINK_EN_A, 1) | + FIELD_PREP(LINK_EN_B, 1)); + serdes_set_bits(serdes, 0x0010, + RESET_ONESHOT | AUTO_LINK | LINK_CFG, + FIELD_PREP(RESET_ONESHOT, 1) | + FIELD_PREP(AUTO_LINK, 0) | + FIELD_PREP(LINK_CFG, SPLITTER_MODE)); + SERDES_DBG_CHIP("%s: change to use split mode\n", __func__); + } + + ret = regmap_read_poll_timeout(serdes->regmap, 0x0013, val, + val & LOCKED, 100, + 50 * USEC_PER_MSEC); + if (ret < 0) { + dev_err(serdes->dev, "GMSL2 link lock timeout\n"); + return ret; + } + + return 0; +} + +static int max96789_deselect(struct serdes *serdes, int chan) +{ + //serdes_set_bits(serdes, 0x0001, DIS_REM_CC, + // FIELD_PREP(DIS_REM_CC, 1)); + + return 0; +} + +static struct serdes_chip_split_ops max96789_split_ops = { + .select = max96789_select, + .deselect = max96789_deselect, +}; + static int max96789_pm_suspend(struct serdes *serdes) { return 0; @@ -719,6 +802,7 @@ struct serdes_chip_data serdes_max96789_data = { .bridge_ops = &max96789_bridge_ops, .pinctrl_ops = &max96789_pinctrl_ops, .gpio_ops = &max96789_gpio_ops, + .split_ops = &max96789_split_ops, .pm_ops = &max96789_pm_ops, .irq_ops = &max96789_irq_ops, }; diff --git a/drivers/mfd/display-serdes/maxim/maxim-max96789.h b/drivers/mfd/display-serdes/maxim/maxim-max96789.h index 7026e506dc81..97beb6dc859a 100644 --- a/drivers/mfd/display-serdes/maxim/maxim-max96789.h +++ b/drivers/mfd/display-serdes/maxim/maxim-max96789.h @@ -39,6 +39,15 @@ #define UART_2_EN BIT(5) #define UART_1_EN BIT(4) +/* 0004h */ +#define GMSL2_B BIT(7) +#define GMSL2_A BIT(6) +#define LINK_EN_B BIT(5) +#define LINK_EN_A BIT(4) +#define AUD_TX_SRC_Y BIT(1) +#define AUD_TX_SRC_X BIT(0) + + /* 0005h */ #define LOCK_EN BIT(7) #define ERRB_EN BIT(6) diff --git a/drivers/mfd/display-serdes/serdes-bridge-split.c b/drivers/mfd/display-serdes/serdes-bridge-split.c new file mode 100644 index 000000000000..38d94692a327 --- /dev/null +++ b/drivers/mfd/display-serdes/serdes-bridge-split.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * serdes-bridge.c -- drm bridge access for different serdes chips + * + * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd. + * + * Author: luowei + */ + +#include "core.h" + +static struct serdes_bridge_split *to_serdes_bridge_split(struct drm_bridge *bridge) +{ + return container_of(bridge, struct serdes_bridge_split, base_bridge); +} + +static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge_split *serdes_bridge_split, + struct device_node *remote_node) +{ + struct mipi_dsi_device_info info = { "serdes", 0, NULL }; + struct serdes *serdes = serdes_bridge_split->parent; + struct mipi_dsi_device *dsi; + struct mipi_dsi_host *host; + int ret; + + if (serdes->chip_data->name) + memcpy(&info.type, serdes->chip_data->name, ARRAY_SIZE(info.type)); + + SERDES_DBG_MFD("%s: type=%s, name=%s\n", __func__, + info.type, serdes->chip_data->name); + + host = of_find_mipi_dsi_host_by_node(remote_node); + if (!host) { + dev_err(serdes_bridge_split->dev, "failed to find serdes dsi host\n"); + return ERR_PTR(-EPROBE_DEFER); + } + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) { + dev_err(serdes_bridge_split->dev, "failed to create serdes dsi device\n"); + return dsi; + } + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + + if (serdes->chip_data->name) { + if ((!strcmp(serdes->chip_data->name, "bu18tl82")) || + (!strcmp(serdes->chip_data->name, "bu18rl82"))) { + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET; + SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_BURST 0x%lx\n", + __func__, serdes->chip_data->name, dsi->mode_flags); + } else { + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_SYNC_PULSE 0x%lx\n", + __func__, serdes->chip_data->name, dsi->mode_flags); + } + } else { + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; + SERDES_DBG_MFD("%s: %s dsi_mode MIPI_DSI_MODE_VIDEO_SYNC_PULSE 0x%lx\n", + __func__, serdes->chip_data->name, dsi->mode_flags); + } + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(serdes_bridge_split->dev, "failed to attach serdes dsi to host\n"); + mipi_dsi_device_unregister(dsi); + return ERR_PTR(ret); + } + + return dsi; +} + +static int serdes_bridge_split_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + struct serdes *serdes = serdes_bridge_split->parent; + int ret = 0; + + ret = drm_of_find_panel_or_bridge(bridge->of_node, 1, -1, + &serdes_bridge_split->panel, + &serdes_bridge_split->next_bridge); + if (ret) { + dev_err(serdes_bridge_split->dev->parent, + "failed to find serdes bridge, ret=%d\n", ret); + return ret; + } + + if (serdes_bridge_split->sel_mipi) { + dev_info(serdes_bridge_split->dev->parent, "serdes sel_mipi %d\n", + serdes_bridge_split->sel_mipi); + /* Attach primary DSI */ + serdes_bridge_split->dsi = serdes_attach_dsi(serdes_bridge_split, + serdes_bridge_split->remote_node); + if (IS_ERR(serdes_bridge_split->dsi)) + return PTR_ERR(serdes_bridge_split->dsi); + } + + if (serdes_bridge_split->next_bridge) { + ret = drm_bridge_attach(bridge->encoder, serdes_bridge_split->next_bridge, + bridge, flags); + if (ret) { + if (serdes_bridge_split->sel_mipi) + mipi_dsi_device_unregister(serdes_bridge_split->dsi); + + dev_err(serdes_bridge_split->dev->parent, + "failed to attach bridge, ret=%d\n", ret); + return ret; + } + } + + if (serdes->chip_data->bridge_ops->attach) + ret = serdes->chip_data->bridge_ops->attach(serdes); + + SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret); + + return ret; +} + +static void serdes_bridge_split_detach(struct drm_bridge *bridge) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + + if (serdes_bridge_split->sel_mipi) { + mipi_dsi_detach(serdes_bridge_split->dsi); + mipi_dsi_device_unregister(serdes_bridge_split->dsi); + } + + SERDES_DBG_MFD("%s\n", __func__); +} + +static void serdes_bridge_split_disable(struct drm_bridge *bridge) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + struct serdes *serdes = serdes_bridge_split->parent; + int ret = 0; + + if (serdes_bridge_split->panel) + drm_panel_disable(serdes_bridge_split->panel); + + if (serdes->chip_data->bridge_ops->disable) + ret = serdes->chip_data->bridge_ops->disable(serdes); + + extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, false); + + SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret); +} + +static void serdes_bridge_split_post_disable(struct drm_bridge *bridge) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + struct serdes *serdes = serdes_bridge_split->parent; + int ret = 0; + + serdes_set_pinctrl_sleep(serdes); + + if (serdes_bridge_split->panel) + ret = drm_panel_unprepare(serdes_bridge_split->panel); + + if (serdes->chip_data->bridge_ops->post_disable) + ret = serdes->chip_data->bridge_ops->post_disable(serdes); + + SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret); +} + +static void serdes_bridge_split_pre_enable(struct drm_bridge *bridge) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + struct serdes *serdes = serdes_bridge_split->parent; + int ret = 0; + + if (serdes->chip_data->bridge_ops->init) + ret = serdes->chip_data->bridge_ops->init(serdes); + + if (serdes->chip_data->serdes_type == TYPE_DES) { + if (serdes->chip_data->chip_init) + serdes->chip_data->chip_init(serdes); + ret = serdes_i2c_set_sequence(serdes); + } + + if (serdes->chip_data->bridge_ops->pre_enable) + ret = serdes->chip_data->bridge_ops->pre_enable(serdes); + + if (serdes_bridge_split->panel) + ret = drm_panel_prepare(serdes_bridge_split->panel); + + serdes_set_pinctrl_default(serdes); + + SERDES_DBG_MFD("%s: %s ret=%d\n", __func__, dev_name(serdes->dev), ret); +} + +static void serdes_bridge_split_enable(struct drm_bridge *bridge) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + struct serdes *serdes = serdes_bridge_split->parent; + int ret = 0; + + if (serdes_bridge_split->panel) + ret = drm_panel_enable(serdes_bridge_split->panel); + + if (serdes->chip_data->bridge_ops->enable) + ret = serdes->chip_data->bridge_ops->enable(serdes); + + if (!ret) { + extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, true); + SERDES_DBG_MFD("%s: extcon is true\n", __func__); + } + + SERDES_DBG_MFD("%s: %s-%s ret=%d\n", __func__, dev_name(serdes->dev), + serdes->chip_data->name, ret); +} + +static enum drm_connector_status +serdes_bridge_split_detect(struct drm_bridge *bridge) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + struct serdes *serdes = serdes_bridge_split->parent; + enum drm_connector_status status = connector_status_connected; + + if (serdes->chip_data->bridge_ops->detect) + status = serdes->chip_data->bridge_ops->detect(serdes); + + return status; +} + +static int serdes_bridge_split_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct serdes_bridge_split *serdes_bridge_split = to_serdes_bridge_split(bridge); + struct serdes *serdes = serdes_bridge_split->parent; + int ret = 0; + + if (serdes->chip_data->bridge_ops->get_modes) + ret = serdes->chip_data->bridge_ops->get_modes(serdes); + + if (serdes_bridge_split->next_bridge) + ret = drm_bridge_get_modes(serdes_bridge_split->next_bridge, connector); + + if (serdes_bridge_split->panel) + ret = drm_panel_get_modes(serdes_bridge_split->panel, connector); + + SERDES_DBG_MFD("%s:name=%s, node=%s\n", __func__, + serdes->chip_data->name, serdes_bridge_split->dev->of_node->name); + + return ret; +} + +static const struct drm_bridge_funcs serdes_bridge_split_funcs = { + .attach = serdes_bridge_split_attach, + .detach = serdes_bridge_split_detach, + .disable = serdes_bridge_split_disable, + .post_disable = serdes_bridge_split_post_disable, + .pre_enable = serdes_bridge_split_pre_enable, + .enable = serdes_bridge_split_enable, + .detect = serdes_bridge_split_detect, + .get_modes = serdes_bridge_split_get_modes, + .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_reset = drm_atomic_helper_bridge_reset, +}; + +static int serdes_bridge_split_probe(struct platform_device *pdev) +{ + struct serdes *serdes = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct serdes_bridge_split *serdes_bridge_split; + + if (!serdes->dev) + return -1; + + serdes_bridge_split = devm_kzalloc(dev, sizeof(*serdes_bridge_split), GFP_KERNEL); + if (!serdes_bridge_split) + return -ENOMEM; + + serdes->serdes_bridge_split = serdes_bridge_split; + serdes_bridge_split->dev = dev; + serdes_bridge_split->parent = dev_get_drvdata(dev->parent); + platform_set_drvdata(pdev, serdes_bridge_split); + serdes_bridge_split->regmap = dev_get_regmap(dev->parent, NULL); + if (!serdes_bridge_split->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n"); + + serdes_bridge_split->sel_mipi = of_property_read_bool(dev->parent->of_node, "sel-mipi"); + SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge_split->sel_mipi); + + serdes_bridge_split->base_bridge.of_node = dev->parent->of_node; + serdes_bridge_split->remote_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1); + if (!serdes_bridge_split->remote_node) { + serdes_bridge_split->base_bridge.of_node = dev->of_node; + SERDES_DBG_MFD("warning: failed to get remote node for serdes on %s\n", + dev_name(dev->parent)); + serdes_bridge_split->remote_node = of_graph_get_remote_node(dev->of_node, 0, -1); + if (!serdes_bridge_split->remote_node) { + return dev_err_probe(dev, -ENODEV, + "failed to get remote node for serdes dsi\n"); + } + } + + serdes_bridge_split->base_bridge.funcs = &serdes_bridge_split_funcs; + serdes_bridge_split->base_bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES; + + if (serdes_bridge_split->sel_mipi) { + serdes_bridge_split->base_bridge.type = DRM_MODE_CONNECTOR_DSI; + SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_DSI\n", __func__); + } else if (serdes_bridge_split->parent->chip_data->connector_type) { + serdes_bridge_split->base_bridge.type = + serdes_bridge_split->parent->chip_data->connector_type; + SERDES_DBG_MFD("%s: type %d\n", __func__, serdes_bridge_split->base_bridge.type); + } else { + serdes_bridge_split->base_bridge.type = DRM_MODE_CONNECTOR_eDP; + SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_LVDS\n", __func__); + } + + drm_bridge_add(&serdes_bridge_split->base_bridge); + + dev_info(dev, "serdes %s, %s successful mipi=%d, of_node=%s\n", + serdes->chip_data->name, __func__, serdes_bridge_split->sel_mipi, + serdes_bridge_split->base_bridge.of_node->name); + + return 0; +} + +static int serdes_bridge_split_remove(struct platform_device *pdev) +{ + struct serdes_bridge_split *serdes_bridge_split = platform_get_drvdata(pdev); + + drm_bridge_remove(&serdes_bridge_split->base_bridge); + + return 0; +} + +static const struct of_device_id serdes_bridge_split_of_match[] = { + { .compatible = "rohm,bu18tl82-bridge-split", }, + { .compatible = "rohm,bu18rl82-bridge-split", }, + { .compatible = "maxim,max96745-bridge-split", }, + { .compatible = "maxim,max96755-bridge-split", }, + { .compatible = "maxim,max96752-bridge-split", }, + { .compatible = "maxim,max96789-bridge-split", }, + { .compatible = "rockchip,rkx111-bridge-split", }, + { .compatible = "rockchip,rkx121-bridge-split", }, + { } +}; + +static struct platform_driver serdes_bridge_split_driver = { + .driver = { + .name = "serdes-bridge-split", + .of_match_table = of_match_ptr(serdes_bridge_split_of_match), + }, + .probe = serdes_bridge_split_probe, + .remove = serdes_bridge_split_remove, +}; + +static int __init serdes_bridge_split_init(void) +{ + return platform_driver_register(&serdes_bridge_split_driver); +} +device_initcall(serdes_bridge_split_init); + +static void __exit serdes_bridge_split_exit(void) +{ + platform_driver_unregister(&serdes_bridge_split_driver); +} +module_exit(serdes_bridge_split_exit); + +MODULE_AUTHOR("Luo Wei "); +MODULE_DESCRIPTION("display bridge interface for different serdes"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:serdes-bridge-split"); diff --git a/drivers/mfd/display-serdes/serdes-bridge.c b/drivers/mfd/display-serdes/serdes-bridge.c index a9448e609315..6dc82a8640be 100644 --- a/drivers/mfd/display-serdes/serdes-bridge.c +++ b/drivers/mfd/display-serdes/serdes-bridge.c @@ -15,7 +15,7 @@ static struct serdes_bridge *to_serdes_bridge(struct drm_bridge *bridge) } static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge *serdes_bridge, - struct device_node *dsi_node) + struct device_node *remote_node) { struct mipi_dsi_device_info info = { "serdes", 0, NULL }; struct serdes *serdes = serdes_bridge->parent; @@ -29,7 +29,7 @@ static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge *serdes_br SERDES_DBG_MFD("%s: type=%s, name=%s\n", __func__, info.type, serdes->chip_data->name); - host = of_find_mipi_dsi_host_by_node(dsi_node); + host = of_find_mipi_dsi_host_by_node(remote_node); if (!host) { dev_err(serdes_bridge->dev, "failed to find serdes dsi host\n"); return ERR_PTR(-EPROBE_DEFER); @@ -90,7 +90,7 @@ static int serdes_bridge_attach(struct drm_bridge *bridge, dev_info(serdes_bridge->dev->parent, "serdes sel_mipi %d\n", serdes_bridge->sel_mipi); /* Attach primary DSI */ - serdes_bridge->dsi = serdes_attach_dsi(serdes_bridge, serdes_bridge->dsi_node); + serdes_bridge->dsi = serdes_attach_dsi(serdes_bridge, serdes_bridge->remote_node); if (IS_ERR(serdes_bridge->dsi)) return PTR_ERR(serdes_bridge->dsi); } @@ -281,17 +281,22 @@ static int serdes_bridge_probe(struct platform_device *pdev) return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n"); serdes_bridge->sel_mipi = of_property_read_bool(dev->parent->of_node, "sel-mipi"); - if (serdes_bridge->sel_mipi) { - serdes_bridge->dsi_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1); - if (!serdes_bridge->dsi_node) - return dev_err_probe(dev->parent, -ENODEV, - "failed to get remote node for serdes dsi\n"); + SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge->sel_mipi); - SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge->sel_mipi); + serdes_bridge->base_bridge.of_node = dev->parent->of_node; + serdes_bridge->remote_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1); + if (!serdes_bridge->remote_node) { + serdes_bridge->base_bridge.of_node = dev->of_node; + SERDES_DBG_MFD("warning: failed to get remote node for serdes on %s\n", + dev_name(dev->parent)); + serdes_bridge->remote_node = of_graph_get_remote_node(dev->of_node, 0, -1); + if (!serdes_bridge->remote_node) { + return dev_err_probe(dev, -ENODEV, + "failed to get remote node for serdes dsi\n"); + } } serdes_bridge->base_bridge.funcs = &serdes_bridge_funcs; - serdes_bridge->base_bridge.of_node = dev->parent->of_node; serdes_bridge->base_bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES; if (serdes_bridge->sel_mipi) { diff --git a/drivers/mfd/display-serdes/serdes-core.c b/drivers/mfd/display-serdes/serdes-core.c index ed671eb9892c..3d9a6e675673 100644 --- a/drivers/mfd/display-serdes/serdes-core.c +++ b/drivers/mfd/display-serdes/serdes-core.c @@ -62,6 +62,10 @@ static const struct mfd_cell serdes_max96789_devs[] = { .name = "serdes-bridge", .of_compatible = "maxim,max96789-bridge", }, + { + .name = "serdes-bridge-split", + .of_compatible = "maxim,max96789-bridge-split", + }, }; static const struct mfd_cell serdes_max96752_devs[] = { @@ -73,6 +77,10 @@ static const struct mfd_cell serdes_max96752_devs[] = { .name = "serdes-panel", .of_compatible = "maxim,max96752-panel", }, + { + .name = "serdes-panel-split", + .of_compatible = "maxim,max96752-panel-split", + }, }; static const struct mfd_cell serdes_max96772_devs[] = { diff --git a/drivers/mfd/display-serdes/serdes-i2c.c b/drivers/mfd/display-serdes/serdes-i2c.c index 49748d3a8322..951d40d3db50 100644 --- a/drivers/mfd/display-serdes/serdes-i2c.c +++ b/drivers/mfd/display-serdes/serdes-i2c.c @@ -9,6 +9,8 @@ #include "core.h" +static struct serdes *g_serdes_ser_split[MAX_NUM_SERDES_SPLIT]; + int serdes_i2c_set_sequence(struct serdes *serdes) { struct device *dev = serdes->dev; @@ -53,6 +55,42 @@ int serdes_i2c_set_sequence(struct serdes *serdes) } EXPORT_SYMBOL_GPL(serdes_i2c_set_sequence); +static int serdes_set_i2c_address(struct serdes *serdes, u32 reg_hw, u32 reg_use, int link) +{ + int ret = 0; + struct i2c_client *client_split; + struct serdes *serdes_split = serdes->g_serdes_bridge_split; + + if (!serdes_split) { + pr_info("%s: serdes_split is null\n", __func__); + return -1; + } + + client_split = to_i2c_client(serdes->regmap->dev); + SERDES_DBG_MFD("%s: %s-%s addr=0x%x reg_hw=0x%x, reg_use=0x%x serdes_split=0x%p\n", + __func__, dev_name(serdes_split->dev), client_split->name, + client_split->addr, serdes->reg_hw, serdes->reg_use, serdes_split); + + client_split->addr = serdes->reg_hw; + + if (serdes_split && serdes_split->chip_data->split_ops && + serdes_split->chip_data->split_ops->select) + ret = serdes_split->chip_data->split_ops->select(serdes_split, link); + + if (serdes->chip_data->split_ops && serdes->chip_data->split_ops->set_i2c_addr) + serdes->chip_data->split_ops->set_i2c_addr(serdes, reg_use, link); + + if (serdes_split && serdes_split->chip_data->split_ops && + serdes_split->chip_data->split_ops->select) + ret = serdes_split->chip_data->split_ops->select(serdes_split, SER_SPLITTER_MODE); + + client_split->addr = serdes->reg_use; + + serdes_i2c_set_sequence(serdes); + + return ret; +} + static void serdes_mfd_work(struct work_struct *work) { struct serdes *serdes = container_of(work, struct serdes, mfd_delay_work.work); @@ -149,7 +187,8 @@ static int serdes_i2c_probe(struct i2c_client *client, serdes->chip_data = (struct serdes_chip_data *)of_device_get_match_data(dev); i2c_set_clientdata(client, serdes); - dev_info(dev, "serdes %s probe start\n", serdes->chip_data->name); + dev_info(dev, "serdes %s probe start, id=%d\n", serdes->chip_data->name, + serdes->chip_data->serdes_id); serdes->type = serdes->chip_data->serdes_type; serdes->regmap = devm_regmap_init_i2c(client, serdes->chip_data->regmap_config); @@ -206,6 +245,31 @@ static int serdes_i2c_probe(struct i2c_client *client, return ret; } + of_property_read_u32(dev->of_node, "id-serdes-bridge-split", + &serdes->id_serdes_bridge_split); + if ((serdes->id_serdes_bridge_split < MAX_NUM_SERDES_SPLIT) && (serdes->type == TYPE_SER)) { + g_serdes_ser_split[serdes->id_serdes_bridge_split] = serdes; + SERDES_DBG_MFD("%s: %s-%s g_serdes_split[%d]=0x%p\n", __func__, + dev_name(serdes->dev), serdes->chip_data->name, + serdes->id_serdes_bridge_split, serdes); + } + + of_property_read_u32(dev->of_node, "reg-hw", &serdes->reg_hw); + of_property_read_u32(dev->of_node, "reg", &serdes->reg_use); + of_property_read_u32(dev->of_node, "link", &serdes->link_use); + of_property_read_u32(dev->of_node, "id-serdes-panel-split", &serdes->id_serdes_panel_split); + if ((serdes->id_serdes_panel_split) && (serdes->type == TYPE_DES)) { + serdes->g_serdes_bridge_split = g_serdes_ser_split[serdes->id_serdes_panel_split]; + SERDES_DBG_MFD("%s: id=%d p=0x%p\n", __func__, + serdes->id_serdes_panel_split, serdes->g_serdes_bridge_split); + } + + if (serdes->reg_hw) { + SERDES_DBG_MFD("%s: %s start change i2c address from 0x%x to 0x%x\n", + __func__, dev->of_node->name, serdes->reg_hw, serdes->reg_use); + serdes_set_i2c_address(serdes, serdes->reg_hw, serdes->reg_use, serdes->link_use); + } + serdes->use_delay_work = of_property_read_bool(dev->of_node, "use-delay-work"); if (serdes->use_delay_work) { serdes->mfd_wq = alloc_ordered_workqueue("%s", diff --git a/drivers/mfd/display-serdes/serdes-panel-split.c b/drivers/mfd/display-serdes/serdes-panel-split.c new file mode 100644 index 000000000000..537d28233ba4 --- /dev/null +++ b/drivers/mfd/display-serdes/serdes-panel-split.c @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * serdes-panel.c -- drm panel access for different serdes chips + * + * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd. + * + * Author: luowei + */ + +#include "core.h" + +static inline struct serdes_panel_split *to_serdes_panel_split(struct drm_panel *panel) +{ + return container_of(panel, struct serdes_panel_split, panel); +} + +static int serdes_panel_split_prepare(struct drm_panel *panel) +{ + struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel); + struct serdes *serdes = serdes_panel_split->parent; + int ret = 0; + + if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->init) + ret = serdes->chip_data->panel_ops->init(serdes); + + if (serdes->chip_data->serdes_type == TYPE_DES) + serdes_i2c_set_sequence(serdes); + + if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->prepare) + ret = serdes->chip_data->panel_ops->prepare(serdes); + + serdes_set_pinctrl_default(serdes); + + SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name); + + return ret; +} + +static int serdes_panel_split_unprepare(struct drm_panel *panel) +{ + struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel); + struct serdes *serdes = serdes_panel_split->parent; + int ret = 0; + + if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->unprepare) + ret = serdes->chip_data->panel_ops->unprepare(serdes); + + serdes_set_pinctrl_sleep(serdes); + + SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name); + + return ret; +} + +static int serdes_panel_split_enable(struct drm_panel *panel) +{ + struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel); + struct serdes *serdes = serdes_panel_split->parent; + int ret = 0; + + if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->enable) + ret = serdes->chip_data->panel_ops->enable(serdes); + + backlight_enable(serdes_panel_split->backlight); + + SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name); + + return ret; +} + +static int serdes_panel_split_disable(struct drm_panel *panel) +{ + struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel); + struct serdes *serdes = serdes_panel_split->parent; + int ret = 0; + + if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->disable) + ret = serdes->chip_data->panel_ops->disable(serdes); + + backlight_disable(serdes_panel_split->backlight); + + SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name); + + return ret; +} + +static int serdes_panel_split_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct serdes_panel_split *serdes_panel_split = to_serdes_panel_split(panel); + struct serdes *serdes = serdes_panel_split->parent; + struct drm_display_mode *mode; + u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; + int ret = 1; + + connector->display_info.width_mm = serdes_panel_split->width_mm; //323; //346; + connector->display_info.height_mm = serdes_panel_split->height_mm; //182; //194; + drm_display_info_set_bus_formats(&connector->display_info, &bus_format, 1); + + mode = drm_mode_duplicate(connector->dev, &serdes_panel_split->mode); + mode->width_mm = serdes_panel_split->width_mm; //323; //346; + mode->height_mm = serdes_panel_split->height_mm; //182; //194; + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->get_modes) + ret = serdes->chip_data->panel_ops->get_modes(serdes); + + pr_info("%s: %s wxh=%dx%d mode clock %u kHz, flags[0x%x]\n" + " H: %04d %04d %04d %04d\n" + " V: %04d %04d %04d %04d\n" + "bus_format: 0x%x\n", + dev_name(serdes->dev), + panel->dev->of_node->name, + serdes_panel_split->width_mm, serdes_panel_split->height_mm, + mode->clock, mode->flags, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, + bus_format); + + return ret; +} + +static const struct drm_panel_funcs serdes_panel_split_funcs = { + .prepare = serdes_panel_split_prepare, + .unprepare = serdes_panel_split_unprepare, + .enable = serdes_panel_split_enable, + .disable = serdes_panel_split_disable, + .get_modes = serdes_panel_split_get_modes, +}; + +static int serdes_panel_split_parse_dt(struct serdes_panel_split *serdes_panel_split) +{ + struct device *dev = serdes_panel_split->dev; + struct display_timing dt; + struct videomode vm; + int ret, len; + unsigned int panel_size[2] = {320, 180}; + unsigned int link_rate_count_ssc[3] = {DP_LINK_BW_2_7, 4, 0}; + + //pr_info("%s: node=%s\n", __func__, dev->of_node->name); + + serdes_panel_split->width_mm = panel_size[0]; + serdes_panel_split->height_mm = panel_size[1]; + + serdes_panel_split->link_rate = link_rate_count_ssc[0]; + serdes_panel_split->lane_count = link_rate_count_ssc[1]; + serdes_panel_split->ssc = link_rate_count_ssc[2]; + + if (of_find_property(dev->of_node, "panel-size", &len)) { + len /= sizeof(unsigned int); + ret = of_property_read_u32_array(dev->of_node, "panel-size", + panel_size, len); + if (!ret) { + serdes_panel_split->width_mm = panel_size[0]; + serdes_panel_split->height_mm = panel_size[1]; + } + } + + if (of_find_property(dev->of_node, "rate-count-ssc", &len)) { + len /= sizeof(unsigned int); + ret = of_property_read_u32_array(dev->of_node, "rate-count-ssc", + panel_size, len); + if (!ret) { + serdes_panel_split->link_rate = link_rate_count_ssc[0]; + serdes_panel_split->lane_count = link_rate_count_ssc[1]; + serdes_panel_split->ssc = link_rate_count_ssc[2]; + } + } + + dev_info(dev, "panle size %dx%d, rate=%d, cnt=%d, ssc=%d\n", + serdes_panel_split->width_mm, serdes_panel_split->height_mm, + serdes_panel_split->link_rate, serdes_panel_split->lane_count, + serdes_panel_split->ssc); + + ret = of_get_display_timing(dev->of_node, "panel-timing", &dt); + if (ret < 0) { + dev_err(dev, "%pOF:serdes no panel-timing node found\n", dev->of_node); + return ret; + } + + videomode_from_timing(&dt, &vm); + drm_display_mode_from_videomode(&vm, &serdes_panel_split->mode); + + return 0; +} + +static int serdes_panel_split_probe(struct platform_device *pdev) +{ + struct serdes *serdes = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct serdes_panel_split *serdes_panel_split; + int ret; + + serdes_panel_split = devm_kzalloc(dev, sizeof(*serdes_panel_split), GFP_KERNEL); + if (!serdes_panel_split) + return -ENOMEM; + + serdes->serdes_panel_split = serdes_panel_split; + serdes_panel_split->dev = dev; + serdes_panel_split->parent = dev_get_drvdata(dev->parent); + platform_set_drvdata(pdev, serdes_panel_split); + + serdes_panel_split->regmap = dev_get_regmap(dev->parent, NULL); + if (!serdes_panel_split->regmap) + return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n"); + + ret = serdes_panel_split_parse_dt(serdes_panel_split); + if (ret) + return dev_err_probe(dev, ret, "failed to parse serdes DT\n"); + + serdes_panel_split->backlight = devm_of_find_backlight(dev); + if (IS_ERR(serdes_panel_split->backlight)) + return dev_err_probe(dev, PTR_ERR(serdes_panel_split->backlight), + "failed to get serdes backlight\n"); + + if (serdes_panel_split->parent->chip_data->connector_type) { + drm_panel_init(&serdes_panel_split->panel, dev, &serdes_panel_split_funcs, + serdes_panel_split->parent->chip_data->connector_type); + } else { + drm_panel_init(&serdes_panel_split->panel, dev, &serdes_panel_split_funcs, + DRM_MODE_CONNECTOR_LVDS); + } + drm_panel_add(&serdes_panel_split->panel); + + dev_info(dev, "serdes %s-%s %s successful\n", dev_name(serdes->dev), + serdes->chip_data->name, __func__); + + return 0; +} + +static int serdes_panel_split_remove(struct platform_device *pdev) +{ + struct serdes_panel_split *serdes_panel_split = platform_get_drvdata(pdev); + + drm_panel_remove(&serdes_panel_split->panel); + + return 0; +} + +static const struct of_device_id serdes_panel_split_of_match[] = { + { .compatible = "rohm,bu18rl82-panel-split" }, + { .compatible = "maxim,max96752-panel-split" }, + { .compatible = "maxim,max96772-panel-split" }, + { .compatible = "rockchip,rkx121-panel-split" }, + { } +}; + +static struct platform_driver serdes_panel_split_driver = { + .driver = { + .name = "serdes-panel-split", + .of_match_table = of_match_ptr(serdes_panel_split_of_match), + }, + .probe = serdes_panel_split_probe, + .remove = serdes_panel_split_remove, +}; + +static int __init serdes_panel_split_init(void) +{ + return platform_driver_register(&serdes_panel_split_driver); +} +device_initcall(serdes_panel_split_init); + +static void __exit serdes_panel_split_exit(void) +{ + platform_driver_unregister(&serdes_panel_split_driver); +} +module_exit(serdes_panel_split_exit); + +MODULE_AUTHOR("Luo Wei "); +MODULE_DESCRIPTION("display panel interface for different serdes"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:serdes-panel-split"); diff --git a/drivers/mfd/display-serdes/serdes-panel.c b/drivers/mfd/display-serdes/serdes-panel.c index 2a8b9357c227..2793220cc691 100644 --- a/drivers/mfd/display-serdes/serdes-panel.c +++ b/drivers/mfd/display-serdes/serdes-panel.c @@ -141,6 +141,8 @@ static int serdes_panel_parse_dt(struct serdes_panel *serdes_panel) unsigned int panel_size[2] = {320, 180}; unsigned int link_rate_count_ssc[3] = {DP_LINK_BW_2_7, 4, 0}; + //pr_info("%s: node=%s\n", __func__, dev->of_node->name); + serdes_panel->width_mm = panel_size[0]; serdes_panel->height_mm = panel_size[1]; @@ -233,7 +235,8 @@ static int serdes_panel_probe(struct platform_device *pdev) } drm_panel_add(&serdes_panel->panel); - dev_info(dev, "serdes %s serdes_panel_probe successful\n", serdes->chip_data->name); + dev_info(dev, "serdes %s-%s serdes_panel_probe successful\n", + dev_name(serdes->dev), serdes->chip_data->name); return 0; } diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h b/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h index 8ade19e23d28..7af000b42785 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h @@ -130,6 +130,8 @@ struct csi2_dphy_hw { int (*stream_off)(struct csi2_dphy *dphy, struct v4l2_subdev *sd); int (*ttl_mode_enable)(struct csi2_dphy_hw *hw); void (*ttl_mode_disable)(struct csi2_dphy_hw *hw); + int (*quick_stream_on)(struct csi2_dphy *dphy, struct v4l2_subdev *sd); + int (*quick_stream_off)(struct csi2_dphy *dphy, struct v4l2_subdev *sd); }; int rockchip_csi2_dphy_hw_init(void); diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c b/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c index 3a7b248b752c..912bb22fbca2 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c @@ -902,13 +902,90 @@ static int csi2_dphy_hw_stream_off(struct csi2_dphy *dphy, write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, 0x01); csi2_dphy_hw_do_reset(hw); - usleep_range(500, 1000); mutex_unlock(&hw->mutex); return 0; } +static int csi2_dphy_hw_quick_stream_on(struct csi2_dphy *dphy, + struct v4l2_subdev *sd) +{ + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd); + struct csi2_sensor *sensor; + struct csi2_dphy_hw *hw = dphy->dphy_hw; + u32 val = 0, pre_val = 0; + + if (!sensor_sd) + return -ENODEV; + sensor = sd_to_sensor(dphy, sensor_sd); + if (!sensor) + return -ENODEV; + + read_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, &pre_val); + if (hw->lane_mode == LANE_MODE_FULL) { + val |= (GENMASK(sensor->lanes - 1, 0) << + CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT) | + (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT); + } else { + if (!(pre_val & (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT))) + val |= (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT); + + if (dphy->phy_index % 3 == DPHY1) + val |= (GENMASK(sensor->lanes - 1, 0) << + CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT); + + if (dphy->phy_index % 3 == DPHY2) { + val |= (GENMASK(sensor->lanes - 1, 0) << + CSI2_DPHY_CTRL_DATALANE_SPLIT_LANE2_3_OFFSET_BIT); + if (hw->drv_data->chip_id >= CHIP_ID_RK3588) + write_csi2_dphy_reg(hw, CSI2PHY_CLK1_LANE_ENABLE, BIT(6)); + } + } + pre_val |= val; + write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, pre_val); + return 0; +} + +static int csi2_dphy_hw_quick_stream_off(struct csi2_dphy *dphy, + struct v4l2_subdev *sd) +{ + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd); + struct csi2_sensor *sensor; + struct csi2_dphy_hw *hw = dphy->dphy_hw; + u32 val = 0, pre_val = 0; + + if (!sensor_sd) + return -ENODEV; + sensor = sd_to_sensor(dphy, sensor_sd); + if (!sensor) + return -ENODEV; + + read_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, &pre_val); + if (hw->lane_mode == LANE_MODE_FULL) { + val |= (GENMASK(sensor->lanes - 1, 0) << + CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT) | + (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT); + } else { + if (!(pre_val & (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT))) + val |= (0x1 << CSI2_DPHY_CTRL_CLKLANE_ENABLE_OFFSET_BIT); + + if (dphy->phy_index % 3 == DPHY1) + val |= (GENMASK(sensor->lanes - 1, 0) << + CSI2_DPHY_CTRL_DATALANE_ENABLE_OFFSET_BIT); + + if (dphy->phy_index % 3 == DPHY2) { + val |= (GENMASK(sensor->lanes - 1, 0) << + CSI2_DPHY_CTRL_DATALANE_SPLIT_LANE2_3_OFFSET_BIT); + if (hw->drv_data->chip_id >= CHIP_ID_RK3588) + write_csi2_dphy_reg(hw, CSI2PHY_CLK1_LANE_ENABLE, BIT(6)); + } + } + pre_val &= ~val; + write_csi2_dphy_reg(hw, CSI2PHY_REG_CTRL_LANE_ENABLE, pre_val); + return 0; +} + static int csi2_dphy_hw_ttl_mode_enable(struct csi2_dphy_hw *hw) { int ret = 0; @@ -1091,6 +1168,8 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev) } dphy_hw->stream_on = drv_data->stream_on; dphy_hw->stream_off = drv_data->stream_off; + dphy_hw->quick_stream_on = csi2_dphy_hw_quick_stream_on; + dphy_hw->quick_stream_off = csi2_dphy_hw_quick_stream_off; if (drv_data->chip_id == CHIP_ID_RV1106) { dphy_hw->ttl_mode_enable = csi2_dphy_hw_ttl_mode_enable; diff --git a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c index b02a3a7cbaa5..7b7556fc1798 100644 --- a/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c +++ b/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c @@ -160,6 +160,7 @@ static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy, int csi_idx, int if (csi_idx < 2) { dcphy_hw = dphy->samsung_phy_group[csi_idx]; mutex_lock(&dcphy_hw->mutex); + dcphy_hw->dphy_dev[dcphy_hw->dphy_dev_num] = dphy; dcphy_hw->dphy_dev_num++; mutex_unlock(&dcphy_hw->mutex); dphy->samsung_phy = dcphy_hw; @@ -199,6 +200,7 @@ static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy, int csi_idx, int else dphy->phy_index = 5; } + dphy_hw->dphy_dev[dphy_hw->dphy_dev_num] = dphy; dphy_hw->dphy_dev_num++; dphy->dphy_hw = dphy_hw; dphy->phy_hw[index] = (void *)dphy_hw; @@ -253,12 +255,56 @@ static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy, int csi_idx, int return 0; } +static void rockchip_csi2_samsung_phy_remove_dphy_dev(struct csi2_dphy *dphy, + struct samsung_mipi_dcphy *dcphy_hw) +{ + int i = 0; + bool is_find_dev = false; + struct csi2_dphy *csi2_dphy = NULL; + + for (i = 0; i < dcphy_hw->dphy_dev_num; i++) { + csi2_dphy = dcphy_hw->dphy_dev[i]; + if (csi2_dphy && + csi2_dphy->phy_index == dphy->phy_index) + is_find_dev = true; + if (is_find_dev) { + if (i < dcphy_hw->dphy_dev_num - 1) + dcphy_hw->dphy_dev[i] = dcphy_hw->dphy_dev[i + 1]; + else + dcphy_hw->dphy_dev[i] = NULL; + } + } + if (is_find_dev) + dcphy_hw->dphy_dev_num--; +} + +static void rockchip_csi2_inno_phy_remove_dphy_dev(struct csi2_dphy *dphy, + struct csi2_dphy_hw *dphy_hw) +{ + int i = 0; + bool is_find_dev = false; + struct csi2_dphy *csi2_dphy = NULL; + + for (i = 0; i < dphy_hw->dphy_dev_num; i++) { + csi2_dphy = dphy_hw->dphy_dev[i]; + if (csi2_dphy && + csi2_dphy->phy_index == dphy->phy_index) + is_find_dev = true; + if (is_find_dev) { + if (i < dphy_hw->dphy_dev_num - 1) + dphy_hw->dphy_dev[i] = dphy_hw->dphy_dev[i + 1]; + else + dphy_hw->dphy_dev[i] = NULL; + } + } + if (is_find_dev) + dphy_hw->dphy_dev_num--; +} + static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int index) { struct csi2_dphy_hw *dphy_hw = NULL; struct samsung_mipi_dcphy *dcphy_hw = NULL; - struct csi2_dphy *csi2_dphy = NULL; - int i = 0; if (dphy->drv_data->chip_id == CHIP_ID_RK3568 || dphy->drv_data->chip_id == CHIP_ID_RV1106) { @@ -269,15 +315,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int return -EINVAL; } mutex_lock(&dphy_hw->mutex); - for (i = 0; i < dphy_hw->dphy_dev_num; i++) { - csi2_dphy = dphy_hw->dphy_dev[i]; - if (csi2_dphy && - csi2_dphy->phy_index == dphy->phy_index) { - dphy_hw->dphy_dev[i] = NULL; - dphy_hw->dphy_dev_num--; - break; - } - } + rockchip_csi2_inno_phy_remove_dphy_dev(dphy, dphy_hw); mutex_unlock(&dphy_hw->mutex); } else if (dphy->drv_data->chip_id == CHIP_ID_RK3588) { if (csi_idx < 2) { @@ -288,7 +326,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int return -EINVAL; } mutex_lock(&dcphy_hw->mutex); - dcphy_hw->dphy_dev_num--; + rockchip_csi2_samsung_phy_remove_dphy_dev(dphy, dcphy_hw); mutex_unlock(&dcphy_hw->mutex); } else { dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[index]; @@ -298,7 +336,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int return -EINVAL; } mutex_lock(&dphy_hw->mutex); - dphy_hw->dphy_dev_num--; + rockchip_csi2_inno_phy_remove_dphy_dev(dphy, dphy_hw); mutex_unlock(&dphy_hw->mutex); } } else { @@ -309,7 +347,7 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int return -EINVAL; } mutex_lock(&dphy_hw->mutex); - dphy_hw->dphy_dev_num--; + rockchip_csi2_inno_phy_remove_dphy_dev(dphy, dphy_hw); mutex_unlock(&dphy_hw->mutex); } @@ -654,6 +692,8 @@ static long rkcif_csi2_dphy_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void { struct csi2_dphy *dphy = to_csi2_dphy(sd); long ret = 0; + int i = 0; + int on = 0; switch (cmd) { case RKCIF_CMD_SET_CSI_IDX: @@ -661,6 +701,24 @@ static long rkcif_csi2_dphy_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void dphy->drv_data->chip_id != CHIP_ID_RV1106) dphy->csi_info = *((struct rkcif_csi_info *)arg); break; + case RKMODULE_SET_QUICK_STREAM: + for (i = 0; i < dphy->csi_info.csi_num; i++) { + if (dphy->csi_info.dphy_vendor[i] == PHY_VENDOR_INNO) { + dphy->dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[i]; + if (!dphy->dphy_hw || + !dphy->dphy_hw->quick_stream_off || + !dphy->dphy_hw->quick_stream_on) { + ret = -EINVAL; + break; + } + on = *(int *)arg; + if (on) + dphy->dphy_hw->quick_stream_on(dphy, sd); + else + dphy->dphy_hw->quick_stream_off(dphy, sd); + } + } + break; default: ret = -ENOIOCTLCMD; break; diff --git a/include/uapi/linux/rkcif-config.h b/include/uapi/linux/rkcif-config.h index f9efbab06b08..c9b9bbf0d301 100644 --- a/include/uapi/linux/rkcif-config.h +++ b/include/uapi/linux/rkcif-config.h @@ -37,7 +37,7 @@ _IOW('V', BASE_VIDIOC_PRIVATE + 7, struct rkcif_csi_info) #define RKCIF_CMD_SET_QUICK_STREAM \ - _IOW('V', BASE_VIDIOC_PRIVATE + 8, int) + _IOWR('V', BASE_VIDIOC_PRIVATE + 8, struct rkcif_quick_stream_param) /* cif memory mode * 0: raw12/raw10/raw8 8bit memory compact @@ -82,4 +82,10 @@ struct rkcif_csi_info { int dphy_vendor[RKCIF_MAX_CSI_NUM]; }; +struct rkcif_quick_stream_param { + int on; + __u32 frame_num; + int resume_mode; +}; + #endif