Skip to content

Porting cmos sensor to mainline next

99degree edited this page May 30, 2024 · 21 revisions

Generally speaking this porting is very limited and soc/board dependent.

A very detail how-to is written by Yassine Oudjana, https://emainline.gitlab.io/2022/08/24/camera.html

The devlopment flow is as below:

  1. get camss sybsys on and dump test pattern from csid. Usually a similar soc is available inside camss.c so copy and paste then modify is a key.
  2. Get camcc and CCI up and running, for cmos sensor communication. And this is independent to camss so either doing this or camss first is free to choose.
  3. Enable debug info in vendor kernel, best is to switch on debug options like
    1. /dev/mem for debugcc direct camcc access and dump clk rate/enablement
    2. debugfs for debug_mdl boot param debug change on the fly
    3. Add pr_info in cam_sensor_module/cam_cci, dump all req that written to CCI, for cmos init reg val. Note[a]
  4. Find relevant cmos driver on other soc especially good source from MTK SOCs.
    1. Find/Search from github.com repo for example, search model name s5k5e9/ov16a1q/ov16a1x/gc8034 or its cmos_id like 559b
  5. Determine topoligy of component connection about cmos_sensor and csiphy (c-phy newer, d-phy only atm for camss)
    1. Lane number, and lane assigment (some case might use abnormal lane assigment instead of incremental e.g. [0 1] and [0 1 2], [0, 1 2 3])
  6. Lane clk determination, according to various source, it is 240000000 as usual both in MTK and qcom soc, and 36000000 is also found a case.
  7. Usual case there are cmos product family, so found a similar driver from mainline/mail-list is a good start. Quite funny example, s5k5e9 cmos have very similar reg pattern (almost identical) with imx214. So lets use it as code base.

It is hightly believe that cmos init reg write also depend on d-phy lane number. So lets make use of those value like dummy.

On some platform using libcamera, there are some important v4l control to present. Implement a dummy control to pass the libcamera check is a good choice too.

The debug information of sensor config is at below, it is a s5k5e9 cmos connected to sm7125 soc, aka miatoll phone. https://github.com/99degree/linux/wiki/developing-qcom-camss#get-more-info-from-los-kernel

and the debug procedure is at: https://github.com/99degree/linux/wiki/developing-qcom-camss#debugging-with-vendor-kernel

Custom kernel bootloop

[a] There is a difficulty due to custom local build kernel vs LOS compiled kernel/OS with fs AVB, it fastboot with local kernel, Android eventually reboot. To get around this, need to build the kernel with nothing changed, replace it to the content of LOS boot.img kernel, repack it and most importantly AVB sign it. It is easier to do with https://github.com/cfig/Android_boot_image_editor, ./gradlew unpack and replace kernel, fastboot boot test, flash to boot partition, factory reset data partition. Test normal boot.

Then apply the pr_info debug log into kernel source and recompile as new one. Regenerate test image as above and do test with "fastboot boot". That should be fine.

Actual log dump of s5k5e9 sensor from Android env

https://github.com/99degree/linux/wiki/s5k5e9-android-log

grab from ab log dump there are initial reg writes, exposure control, framelength etc. grep "cam_cci_data_queue: 738"

Frame dump output

Assumed sensor driver is up and capable to output frame. Here are some info that from linux-media guys provided.

stride length

stride length is every line of pic, got some padding. Set correct stride value otherwise distorted pattern.

Stride value can obtain from yavta running log. Here is an example.

	Device /dev/video0 opened.
	Device `Qualcomm Camera Subsystem' on `platform:acb3000.camss' (driver 'qcom-camss') supports video, capture, with mplanes.
	Video format set: SRGGB10P (41415270) 2592x1944 field none, 1 planes:
	 * Stride 3248, buffer size 6314112
	Video format: SRGGB10P (41415270) 2592x1944 field none, 1 planes:
	 * Stride 3248, buffer size 6314112
	3 buffers requested.
	length: 1 offset: 4147991128 timestamp type/source: mono/EoF
	Buffer 0/0 mapped at address 0xfcaa71600000.
	length: 1 offset: 4147991128 timestamp type/source: mono/EoF
	Buffer 1/0 mapped at address 0xfcaa70e08000.
	length: 1 offset: 4147991128 timestamp type/source: mono/EoF
	Buffer 2/0 mapped at address 0xfcaa70610000.
	0 (0) [-] none 0 6314112 B 3778.180143 3778.182266 10.922 fps ts mono/EoF
	1 (1) [-] none 1 6314112 B 3778.212588 3778.402065 30.821 fps ts mono/EoF
	2 (2) [-] none 2 6314112 B 3778.245863 3778.487352 30.053 fps ts mono/EoF
	Captured 3 frames in 0.398766 seconds (7.523197 fps, 0.000000 B/s).
	3 buffers released.

Calculating settle_cnt for csiphy link_freq

below is a calculation of settle cnt, it might not needed at least sm7125 is using the calculated value, not the one using custom settle_cnt from vendor binary atm.

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
typedef int u8;
typedef int u32;
typedef unsigned long long s64;

static u8 csiphy_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate, int ui_div)
{
		u32 ui; /* ps */
		u32 timer_period; /* ps */
		u32 t_hs_prepare_max; /* ps */
		u32 t_hs_settle; /* ps */
		u8 settle_cnt;

		lldiv_t div_d;

		if (link_freq <= 0)
				return 0;

		div_d = lldiv(1000000000000LL, link_freq);
		ui = div_d.quot;

		//ui = div_u64(1000000000000LL, link_freq);
		ui /= ui_div;

		t_hs_prepare_max = 85000 + 6 * ui;
		t_hs_settle = t_hs_prepare_max;

		div_d = lldiv(1000000000000LL, timer_clk_rate);
		timer_period = div_d.quot;
		//timer_period = div(1000000000000LL, timer_clk_rate);
		settle_cnt = t_hs_settle / timer_period - 6;

		printf("link_freq=%lld, timer_clk=%d, ldiv_link_freq 0x%llx, ui_div=%d, ui_after=0x%x, timer_period=0x%x, settle_cnt=0x%x \n", link_freq, timer_clk_rate, div_d.quot, ui_div, ui, timer_period, settle_cnt);

		return settle_cnt;
}

#define size_of(a) (sizeof(a)/sizeof(a[0]))

int main() {
   // printf() displays the string inside quotation

		s64 link_freq[] = { 240000000, 480000000, 512000000, 600000000} ;
		u32 timer_clk_rate[] = {240000000, 269333333, 300000000, 0};
		int i,j,k;

		for (k = 0; k<size_of(timer_clk_rate);k++)
		for (j = 1; j < 3; j++)
		for (i = 0; i < size_of(link_freq); i++) {
				csiphy_settle_cnt_calc(link_freq[i], timer_clk_rate[k], j);
		}
		return 0;
}

And the result is:

link_freq=240000000, timer_clk=240000000, t_hs_settle=0x1adac, ldiv_link_freq 0x1046, ui_div=1, ui_after=0x1046, timer_period=0x1046, settle_cnt=0x14
link_freq=480000000, timer_clk=240000000, t_hs_settle=0x17cda, ldiv_link_freq 0x1046, ui_div=1, ui_after=0x823, timer_period=0x1046, settle_cnt=0x11
link_freq=512000000, timer_clk=240000000, t_hs_settle=0x179ce, ldiv_link_freq 0x1046, ui_div=1, ui_after=0x7a1, timer_period=0x1046, settle_cnt=0x11
link_freq=600000000, timer_clk=240000000, t_hs_settle=0x17314, ldiv_link_freq 0x1046, ui_div=1, ui_after=0x682, timer_period=0x1046, settle_cnt=0x10
link_freq=240000000, timer_clk=240000000, t_hs_settle=0x17cda, ldiv_link_freq 0x1046, ui_div=2, ui_after=0x823, timer_period=0x1046, settle_cnt=0x11
link_freq=480000000, timer_clk=240000000, t_hs_settle=0x1646e, ldiv_link_freq 0x1046, ui_div=2, ui_after=0x411, timer_period=0x1046, settle_cnt=0xf
link_freq=512000000, timer_clk=240000000, t_hs_settle=0x162e8, ldiv_link_freq 0x1046, ui_div=2, ui_after=0x3d0, timer_period=0x1046, settle_cnt=0xf
link_freq=600000000, timer_clk=240000000, t_hs_settle=0x15f8e, ldiv_link_freq 0x1046, ui_div=2, ui_after=0x341, timer_period=0x1046, settle_cnt=0xf
link_freq=240000000, timer_clk=269333333, t_hs_settle=0x1adac, ldiv_link_freq 0xe80, ui_div=1, ui_after=0x1046, timer_period=0xe80, settle_cnt=0x17
link_freq=480000000, timer_clk=269333333, t_hs_settle=0x17cda, ldiv_link_freq 0xe80, ui_div=1, ui_after=0x823, timer_period=0xe80, settle_cnt=0x14
link_freq=512000000, timer_clk=269333333, t_hs_settle=0x179ce, ldiv_link_freq 0xe80, ui_div=1, ui_after=0x7a1, timer_period=0xe80, settle_cnt=0x14
link_freq=600000000, timer_clk=269333333, t_hs_settle=0x17314, ldiv_link_freq 0xe80, ui_div=1, ui_after=0x682, timer_period=0xe80, settle_cnt=0x13
link_freq=240000000, timer_clk=269333333, t_hs_settle=0x17cda, ldiv_link_freq 0xe80, ui_div=2, ui_after=0x823, timer_period=0xe80, settle_cnt=0x14
link_freq=480000000, timer_clk=269333333, t_hs_settle=0x1646e, ldiv_link_freq 0xe80, ui_div=2, ui_after=0x411, timer_period=0xe80, settle_cnt=0x12
link_freq=512000000, timer_clk=269333333, t_hs_settle=0x162e8, ldiv_link_freq 0xe80, ui_div=2, ui_after=0x3d0, timer_period=0xe80, settle_cnt=0x12
link_freq=600000000, timer_clk=269333333, t_hs_settle=0x15f8e, ldiv_link_freq 0xe80, ui_div=2, ui_after=0x341, timer_period=0xe80, settle_cnt=0x12
link_freq=240000000, timer_clk=300000000, t_hs_settle=0x1adac, ldiv_link_freq 0xd05, ui_div=1, ui_after=0x1046, timer_period=0xd05, settle_cnt=0x1b
link_freq=480000000, timer_clk=300000000, t_hs_settle=0x17cda, ldiv_link_freq 0xd05, ui_div=1, ui_after=0x823, timer_period=0xd05, settle_cnt=0x17
link_freq=512000000, timer_clk=300000000, t_hs_settle=0x179ce, ldiv_link_freq 0xd05, ui_div=1, ui_after=0x7a1, timer_period=0xd05, settle_cnt=0x17
link_freq=600000000, timer_clk=300000000, t_hs_settle=0x17314, ldiv_link_freq 0xd05, ui_div=1, ui_after=0x682, timer_period=0xd05, settle_cnt=0x16
link_freq=240000000, timer_clk=300000000, t_hs_settle=0x17cda, ldiv_link_freq 0xd05, ui_div=2, ui_after=0x823, timer_period=0xd05, settle_cnt=0x17
link_freq=480000000, timer_clk=300000000, t_hs_settle=0x1646e, ldiv_link_freq 0xd05, ui_div=2, ui_after=0x411, timer_period=0xd05, settle_cnt=0x15
link_freq=512000000, timer_clk=300000000, t_hs_settle=0x162e8, ldiv_link_freq 0xd05, ui_div=2, ui_after=0x3d0, timer_period=0xd05, settle_cnt=0x15
link_freq=600000000, timer_clk=300000000, t_hs_settle=0x15f8e, ldiv_link_freq 0xd05, ui_div=2, ui_after=0x341, timer_period=0xd05, settle_cnt=0x15

Clearly shows that t_hs_settle is domainant with constant 85000.

Generally match with the dump from camss:

joyeuse:/config/qcom_camss # cat   acb3000.camss_msm_csiphy2
Link_freq(0x8) 0x00000015
Link_freq(0x208) 0x00000015
Link_freq(0x408) 0x00000015
Link_freq(0x608) 0x00000015
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0) 0x00000002
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(1) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(2) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(3) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(4) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5) 0x00000085
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(0) 0x00000004
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(1) 0x00000001
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(2) 0x00000080
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(3) 0x00000020
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(4) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(5) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(6) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(7) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(8) 0x00000000
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(9) 0x00000040
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(10) 0x00000004