common/csi: Decouple the clock from main CSI state.

This patch decouples the clock from the main CSI state,
further separating the CSI instance from the port's CSI
driver. Additionally, it allows CSIs to use different clocks,
in theory, though they must first be detected somehow in
order to switch clocks.

As a side effect of sharing the clock, set_frequency will be
called more frequently (the default call plus once per CSI).
However, the frequency is now checked, and the clock is only
reconfigured if the new frequency exceeds a configurable
tolerance.

Finally, get_clk_frequency now accepts a boolean to return
either the exact clock frequency, for sensors that require
it, or the nominal frequency.

Signed-off-by: iabdalkader <i.abdalkader@gmail.com>
This commit is contained in:
iabdalkader 2025-07-03 12:32:43 +02:00
parent 287e387122
commit 2040a0a000
2 changed files with 55 additions and 15 deletions

View File

@ -61,6 +61,10 @@
#define OMV_CSI_I2C_REINIT (1) #define OMV_CSI_I2C_REINIT (1)
#endif #endif
#ifndef OMV_CSI_CLK_TOLERANCE
#define OMV_CSI_CLK_TOLERANCE (500000)
#endif
#ifndef __weak #ifndef __weak
#define __weak __attribute__((weak)) #define __weak __attribute__((weak))
#endif #endif
@ -120,6 +124,7 @@ uint16_t resolution[][2] = {
}; };
static omv_i2c_t csi_i2c; static omv_i2c_t csi_i2c;
static omv_clk_t csi_clk;
omv_csi_t csi_all[OMV_CSI_MAX_DEVICES] = {0}; omv_csi_t csi_all[OMV_CSI_MAX_DEVICES] = {0};
__weak void omv_csi_init0() { __weak void omv_csi_init0() {
@ -178,14 +183,15 @@ __weak int omv_csi_init() {
memset(csi, 0, sizeof(omv_csi_t)); memset(csi, 0, sizeof(omv_csi_t));
csi->i2c = &csi_i2c; csi->i2c = &csi_i2c;
csi->clk = &csi_clk;
csi->fb = framebuffer_get(-1); csi->fb = framebuffer_get(-1);
csi->color_palette = rainbow_table; csi->color_palette = rainbow_table;
omv_csi_ops_init(csi); omv_csi_ops_init(csi);
}
// Configure the csi external clock (XCLK). // Configure the csi external clock (XCLK).
if (omv_csi_set_clk_frequency(OMV_CSI_CLK_FREQUENCY) != 0) { if (omv_csi_set_clk_frequency(csi, OMV_CSI_CLK_FREQUENCY) != 0) {
return OMV_CSI_ERROR_TIM_INIT_FAILED; return OMV_CSI_ERROR_TIM_INIT_FAILED;
}
} }
// Detect and initialize sensor(s). // Detect and initialize sensor(s).
@ -544,10 +550,11 @@ int omv_csi_probe(omv_i2c_t *i2c) {
} }
#endif #endif
// Allow reconfiguring (or disabling) the external clock // Sensors can change the clock's frequency or disable it
// if just one sensor is detected, or for main sensors. // (with clk_hz=0). This is allowed only if the clock is
// not shared (dev_count == 1) or if this is a main sensor.
if (dev_count == 1 || !csi->auxiliary) { if (dev_count == 1 || !csi->auxiliary) {
omv_csi_set_clk_frequency(csi->clk_hz); omv_csi_set_clk_frequency(csi, csi->clk_hz);
} }
// Count aux devices. // Count aux devices.
@ -566,7 +573,7 @@ int omv_csi_probe(omv_i2c_t *i2c) {
// If more than one aux sensor was detected, the clock // If more than one aux sensor was detected, the clock
// hasn't been changed, so reconfigure using this freq. // hasn't been changed, so reconfigure using this freq.
if (dev_count > 1) { if (dev_count > 1) {
omv_csi_set_clk_frequency(csi->clk_hz); omv_csi_set_clk_frequency(csi, csi->clk_hz);
} }
break; break;
} }
@ -605,12 +612,33 @@ __weak int omv_csi_get_id(omv_csi_t *csi) {
return csi->chip_id; return csi->chip_id;
} }
__weak uint32_t omv_csi_get_clk_frequency() { __weak uint32_t omv_csi_get_clk_frequency(omv_csi_t *csi, bool nominal) {
return OMV_CSI_ERROR_CTL_UNSUPPORTED; omv_clk_t *clk = csi->clk;
if (clk->get_freq != NULL && !nominal) {
// Return the exact frequency
return clk->get_freq(clk);
}
// Return nominal frequency
return clk->freq;
} }
__weak int omv_csi_set_clk_frequency(uint32_t frequency) { __weak int omv_csi_set_clk_frequency(omv_csi_t *csi, uint32_t frequency) {
return OMV_CSI_ERROR_CTL_UNSUPPORTED; omv_clk_t *clk = csi->clk;
uint32_t diff_hz = abs(frequency - omv_csi_get_clk_frequency(csi, false));
if (diff_hz <= OMV_CSI_CLK_TOLERANCE) {
return 0;
}
if (clk->set_freq != NULL &&
clk->set_freq(clk, frequency) != 0) {
return OMV_CSI_ERROR_CTL_FAILED;
}
clk->freq = frequency;
return 0;
} }
__weak bool omv_csi_is_detected(omv_csi_t *csi) { __weak bool omv_csi_is_detected(omv_csi_t *csi) {

View File

@ -291,6 +291,17 @@ typedef struct _omv_csi_callback_t {
typedef int (*omv_csi_snapshot_t) typedef int (*omv_csi_snapshot_t)
(omv_csi_t *csi, image_t *image, uint32_t flags); (omv_csi_t *csi, image_t *image, uint32_t flags);
typedef struct _omv_clk omv_clk_t;
typedef struct _omv_clk {
uint32_t freq;
#ifdef OMV_CSI_CLK_PORT_BITS
OMV_CSI_CLK_PORT_BITS
#endif
uint32_t (*get_freq) (omv_clk_t *csi);
int (*set_freq) (omv_clk_t *csi, uint32_t freq);
} omv_clk_t;
typedef struct _omv_csi { typedef struct _omv_csi {
uint32_t chip_id; // Sensor ID 32 bits. uint32_t chip_id; // Sensor ID 32 bits.
uint8_t slv_addr; // Sensor I2C slave address. uint8_t slv_addr; // Sensor I2C slave address.
@ -339,10 +350,11 @@ typedef struct _omv_csi {
bool auto_rotation; // Rotate Image Automatically bool auto_rotation; // Rotate Image Automatically
bool detected; // Set to true when the sensor is initialized. bool detected; // Set to true when the sensor is initialized.
bool power_on; // Set to true when the sensor is active. bool power_on; // Set to true when the sensor is active.
uint32_t clk_hz; // Clock frequency requested by the driver.
omv_i2c_t *i2c; // SCCB/I2C bus. omv_i2c_t *i2c; // SCCB/I2C bus.
framebuffer_t *fb; // Frame buffer pointer framebuffer_t *fb; // Frame buffer pointer
omv_clk_t *clk; // Clock controller.
uint32_t clk_hz; // Clock freqeuency request by this CSI.
#ifdef OMV_CSI_PORT_BITS #ifdef OMV_CSI_PORT_BITS
// Additional port-specific members like device base pointer, // Additional port-specific members like device base pointer,
@ -429,10 +441,10 @@ int omv_csi_reset(omv_csi_t *csi, bool hard);
int omv_csi_get_id(omv_csi_t *csi); int omv_csi_get_id(omv_csi_t *csi);
// Returns the xclk freq in hz. // Returns the xclk freq in hz.
uint32_t omv_csi_get_clk_frequency(); uint32_t omv_csi_get_clk_frequency(omv_csi_t *csi, bool nominal);
// Returns the xclk freq in hz. // Returns the xclk freq in hz.
int omv_csi_set_clk_frequency(uint32_t frequency); int omv_csi_set_clk_frequency(omv_csi_t *csi, uint32_t frequency);
// Return true if the sensor was detected and initialized. // Return true if the sensor was detected and initialized.
bool omv_csi_is_detected(omv_csi_t *csi); bool omv_csi_is_detected(omv_csi_t *csi);