Skip to content

Commit

Permalink
drivers: video: mcux_mipi_csi2rx: Set clocks according to pixel rate
Browse files Browse the repository at this point in the history
Instead of fixing csi2rx clock frequencies, set them according to the
pixel rate got from the camera sensor.

Signed-off-by: Trung Hieu Le <[email protected]>
Signed-off-by: Phi Bang Nguyen <[email protected]>
  • Loading branch information
trunghieulenxp authored and ngphibang committed Aug 21, 2024
1 parent 7be81bf commit cd07809
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 90 deletions.
168 changes: 89 additions & 79 deletions drivers/video/video_mcux_mipi_csi2rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define DT_DRV_COMPAT nxp_mipi_csi2rx

#include <fsl_mipi_csi2rx.h>
#include <soc.h>

#include <zephyr/drivers/video.h>
#include <zephyr/kernel.h>
Expand All @@ -15,7 +16,9 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(mipi_csi);

#define DEFAULT_CAMERA_FRAME_RATE 30
#define RX_HS_SETTLE_MIN 1
/* CSI-2 Rx maximum lane rate is 1.5 Gbps */
#define MAX_LANE_RATE 1500000000

struct mipi_csi2rx_config {
const MIPI_CSI2RX_Type *base;
Expand All @@ -26,98 +29,99 @@ struct mipi_csi2rx_data {
csi2rx_config_t csi2rxConfig;
};

static int mipi_csi2rx_set_fmt(const struct device *dev, enum video_endpoint_id ep,
struct video_format *fmt)
static int mipi_csi2rx_cal_rx_hs_settle(uint64_t lane_rate, uint8_t *tHsSettle_EscClk)
{
const struct mipi_csi2rx_config *config = dev->config;
struct mipi_csi2rx_data *drv_data = dev->data;
csi2rx_config_t csi2rxConfig = {0};
uint8_t i = 0;
uint64_t t_hs_settle_min, t_hs_settle_max, t_hs_settle, rx_hs_settle_cal, t_esc_clk;
uint32_t esc_clk;

esc_clk = CLOCK_GetRootClockFreq(kCLOCK_Root_Csi2_Esc);
if (esc_clk == 0) {
LOG_ERR("Invalid esc clk value");
return -EINVAL;
}

t_esc_clk = NSEC_PER_SEC / esc_clk;

/*
* Initialize the MIPI CSI2
*
* From D-PHY specification, the T-HSSETTLE should in the range of 85ns+6*UI to 145ns+10*UI
* UI is Unit Interval, equal to the duration of any HS state on the Clock Lane
*
* T-HSSETTLE = csi2rxConfig.tHsSettle_EscClk * (Tperiod of RxClkInEsc)
* From MIPI CSI2 D-PHY specification, the T_HS_SETTLE period should be
* in the range of 85ns+6*UI to 145ns+10*UI. The Unit Interval (UI) is
* the period for transmitting one bit for each lane. Since the data is
* transmitted in DDR mode, UI is defined as:
*
* csi2rxConfig.tHsSettle_EscClk setting for camera:
* UI = 1/2 * period of one bit = 1/2 * 1/link_freq
*
* Resolution | frame rate | T_HS_SETTLE
* =============================================
* 720P | 30 | 0x12
* ---------------------------------------------
* 720P | 15 | 0x17
* ---------------------------------------------
* VGA | 30 | 0x1F
* ---------------------------------------------
* VGA | 15 | 0x24
* ---------------------------------------------
* QVGA | 30 | 0x1F
* ---------------------------------------------
* QVGA | 15 | 0x24
* ---------------------------------------------
* Here, we choose a T_HS_SETTLE value which is in the middle of the range.
* Thus, tHsSettle_EscClk is calculated using the following formula:
* csi2rxConfig.tHsSettle_EscClk = T_HS_SETTLE / period of RxClkInEsc
*/
static const uint32_t csi2rxHsSettle[][4] = {
{
1280,
720,
30,
0x12,
},
{
1280,
720,
15,
0x17,
},
{
640,
480,
30,
0x1F,
},
{
640,
480,
15,
0x24,
},
{
320,
240,
30,
0x1F,
},
{
320,
240,
15,
0x24,
},
};

for (i = 0; i < ARRAY_SIZE(csi2rxHsSettle); i++) {
if ((fmt->width == csi2rxHsSettle[i][0]) && (fmt->height == csi2rxHsSettle[i][1]) &&
(DEFAULT_CAMERA_FRAME_RATE == csi2rxHsSettle[i][2])) {
csi2rxConfig.tHsSettle_EscClk = csi2rxHsSettle[i][3];
break;
}
t_hs_settle_min = 85 + 6 * NSEC_PER_SEC / lane_rate;
t_hs_settle_max = 145 + 10 * NSEC_PER_SEC / lane_rate;
t_hs_settle = (t_hs_settle_min + t_hs_settle_max) / 2;
rx_hs_settle_cal = t_hs_settle / t_esc_clk;
if (!IN_RANGE(rx_hs_settle_cal, RX_HS_SETTLE_MIN, UINT8_MAX)) {
LOG_ERR("rx_hs_settle calculated value is not correct %llu", rx_hs_settle_cal);
return -EINVAL;
}

if (i == ARRAY_SIZE(csi2rxHsSettle)) {
LOG_ERR("Unsupported resolution");
return -ENOTSUP;
*tHsSettle_EscClk = (uint8_t)rx_hs_settle_cal;

return 0;
}

static int mipi_csi2rx_update_settings(const struct device *dev, enum video_endpoint_id ep)
{
const struct mipi_csi2rx_config *config = dev->config;
struct mipi_csi2rx_data *drv_data = dev->data;
uint8_t bpp;
uint64_t sensor_pixel_rate, sensor_lane_rate, sensor_byte_clk;
int ret;
struct video_format fmt;

ret = video_get_format(config->sensor_dev, ep, &fmt);
if (ret) {
LOG_ERR("Cannot get sensor_dev pixel format");
return ret;
}

drv_data->csi2rxConfig = csi2rxConfig;
ret = video_get_ctrl(config->sensor_dev, VIDEO_CID_PIXEL_RATE,
(uint64_t *)&sensor_pixel_rate);
if (ret) {
LOG_ERR("Can not get sensor_dev pixel rate");
return ret;
}

bpp = video_pix_fmt_bpp(fmt.pixelformat) * 8;
sensor_lane_rate = sensor_pixel_rate * bpp / drv_data->csi2rxConfig.laneNum;
if (sensor_lane_rate > MAX_LANE_RATE) {
LOG_ERR("Rate per lane is too high (max limit: 1.5 Gbps each lane)");
return -EINVAL;
}

sensor_byte_clk = sensor_pixel_rate * bpp / drv_data->csi2rxConfig.laneNum / 8;
if (sensor_byte_clk >= CLOCK_GetRootClockFreq(kCLOCK_Root_Csi2)) {
mipi_csi2rx_clock_set_freq(kCLOCK_Root_Csi2, sensor_byte_clk);
}

if (sensor_pixel_rate >= CLOCK_GetRootClockFreq(kCLOCK_Root_Csi2_Ui)) {
mipi_csi2rx_clock_set_freq(kCLOCK_Root_Csi2_Ui, sensor_pixel_rate);
}

ret = mipi_csi2rx_cal_rx_hs_settle(sensor_lane_rate,
&drv_data->csi2rxConfig.tHsSettle_EscClk);

return ret;
}

static int mipi_csi2rx_set_fmt(const struct device *dev, enum video_endpoint_id ep,
struct video_format *fmt)
{
const struct mipi_csi2rx_config *config = dev->config;

if (video_set_format(config->sensor_dev, ep, fmt)) {
return -EIO;
}

return 0;
return mipi_csi2rx_update_settings(dev, ep);
}

static int mipi_csi2rx_get_fmt(const struct device *dev, enum video_endpoint_id ep,
Expand Down Expand Up @@ -205,7 +209,13 @@ static int mipi_csi2rx_init(const struct device *dev)
return -ENODEV;
}

return 0;
/*
* CSI2 escape clock should be in the range [60, 80] Mhz. We set it
* to 60 Mhz.
*/
mipi_csi2rx_clock_set_freq(kCLOCK_Root_Csi2_Esc, MHZ(60));

return mipi_csi2rx_update_settings(dev, VIDEO_EP_ANY);
}

#define MIPI_CSI2RX_INIT(n) \
Expand Down
41 changes: 30 additions & 11 deletions soc/nxp/imxrt/imxrt11xx/soc.c
Original file line number Diff line number Diff line change
Expand Up @@ -477,17 +477,6 @@ static ALWAYS_INLINE void clock_init(void)
CLOCK_EnableClock(kCLOCK_Video_Mux);
VIDEO_MUX->VID_MUX_CTRL.SET = VIDEO_MUX_VID_MUX_CTRL_CSI_SEL_MASK;

/* Configure MIPI CSI-2 Rx clocks */
rootCfg.div = 8;
rootCfg.mux = kCLOCK_CSI2_ClockRoot_MuxSysPll3Out;
CLOCK_SetRootClock(kCLOCK_Root_Csi2, &rootCfg);

rootCfg.mux = kCLOCK_CSI2_ESC_ClockRoot_MuxSysPll3Out;
CLOCK_SetRootClock(kCLOCK_Root_Csi2_Esc, &rootCfg);

rootCfg.mux = kCLOCK_CSI2_UI_ClockRoot_MuxSysPll3Out;
CLOCK_SetRootClock(kCLOCK_Root_Csi2_Ui, &rootCfg);

/* Enable power domain for MIPI CSI-2 */
PGMC_BPC4->BPC_POWER_CTRL |= (PGMC_BPC_BPC_POWER_CTRL_PSW_ON_SOFT_MASK |
PGMC_BPC_BPC_POWER_CTRL_ISO_OFF_SOFT_MASK);
Expand Down Expand Up @@ -685,6 +674,36 @@ void imxrt_post_init_display_interface(void)

#endif

#if CONFIG_VIDEO_MCUX_MIPI_CSI2RX
void mipi_csi2rx_clock_set_freq(clock_root_t clock_root, uint32_t rate)
{
clock_root_config_t rootCfg = {0};
uint32_t freq;
clock_name_t clk_source;

switch (clock_root) {
case kCLOCK_Root_Csi2:
rootCfg.mux = kCLOCK_CSI2_ClockRoot_MuxSysPll3Out;
break;
case kCLOCK_Root_Csi2_Esc:
rootCfg.mux = kCLOCK_CSI2_ESC_ClockRoot_MuxSysPll3Out;
break;
case kCLOCK_Root_Csi2_Ui:
rootCfg.mux = kCLOCK_CSI2_UI_ClockRoot_MuxSysPll3Out;
break;
default:
/** Not handle */
return;
}

clk_source = CLOCK_GetRootClockSource(clock_root, rootCfg.mux);
freq = CLOCK_GetFreq(clk_source);
__ASSERT(rate < freq, "Requested rate is higher than the maximum clock frequency");
rootCfg.div = (uint32_t)freq / rate;
CLOCK_SetRootClock(clock_root, &rootCfg);
}
#endif

/**
*
* @brief Perform basic hardware initialization
Expand Down
4 changes: 4 additions & 0 deletions soc/nxp/imxrt/imxrt11xx/soc.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ void imxrt_pre_init_display_interface(void);
void imxrt_post_init_display_interface(void);
#endif

#if CONFIG_VIDEO_MCUX_MIPI_CSI2RX
void mipi_csi2rx_clock_set_freq(clock_root_t clock_root, uint32_t rate);
#endif

void flexspi_clock_set_div(uint32_t value);
uint32_t flexspi_clock_get_freq(void);

Expand Down

0 comments on commit cd07809

Please sign in to comment.