From 6b66ee40d96dd7fbadc27d9426ffaf962cdac970 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 11 Nov 2024 10:54:31 +0800 Subject: [PATCH 1/4] Revert "PCI: rockchip: dw: remove wakeup if attached device is down" This reverts commit e15707617c807e82a706dfdd2240c83d62f2634e. This need the system to rmmod wireless ko before suspend, otherwise even if the power is down but removing wakeup will call the sub-device driver to broadcast this uevent to the userspace which prevent the system from into suspend. Whether to rmmod wireless driver is system depend, so revert this commit. Signed-off-by: Shawn Lin Change-Id: I9f0467a7403c1f18820a89ae4707d3bd469910a6 --- drivers/pci/controller/dwc/pcie-dw-rockchip.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index c6421475bdb2..ba43751b3056 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -1677,8 +1677,7 @@ static int rk_pcie_really_probe(void *p) dw_pcie_dbi_ro_wr_dis(pci); /* 7. framework misc settings */ - if (rk_pcie->skip_scan_in_resume) - device_init_wakeup(dev, true); + device_init_wakeup(dev, true); device_enable_async_suspend(dev); /* Enable async system PM for multiports SoC */ rk_pcie->finish_probe = true; @@ -1914,10 +1913,8 @@ static int __maybe_unused rockchip_dw_pcie_suspend(struct device *dev) */ if (rk_pcie->skip_scan_in_resume) { rfkill_get_wifi_power_state(&power); - if (!power) { - device_init_wakeup(dev, false); + if (!power) goto no_l2; - } } /* 2. Broadcast PME_Turn_Off Message */ @@ -2005,8 +2002,6 @@ static int __maybe_unused rockchip_dw_pcie_resume(struct device *dev) dw_pcie_dbi_ro_wr_dis(pci); rk_pcie->in_suspend = false; - if (rk_pcie->skip_scan_in_resume) - device_init_wakeup(dev, true); return 0; From 0fd102707dd3a6634079863ef8a70c4d578fc037 Mon Sep 17 00:00:00 2001 From: Zhen Chen Date: Mon, 11 Nov 2024 10:30:07 +0800 Subject: [PATCH 2/4] Mali: bifrost: Using Upstream For MT Unmapped Area Topdown-Search Comes from 03490297.diff in mail "241108: 21:51: ARM support: Inchara: ". The original patch contains the following information: { Subject: [PATCH] [Official] GPUCORE-43479 Using Upstream For MT Unmapped Area Topdown-Search This commit addresses the unmapped area search not ending its loop operation issue. The change applies the upstream vm_unmapped_area() with Maple-Tree (kernel 6.1.x) for search operations and then uses a kbase specific adjustment loop for revisions to meet some extra hardware alignment constraints. } The "not ending its loop operation issue" is reflected in various RK tests as a "rcu_sched self-detected stall on CPU" with a call trace within Mali driver. An instance: [ 1442.379022] rcu: INFO: rcu_sched self-detected stall on CPU [ 1442.379028] rcu: 4-....: (14999 ticks this GP) idle=a8b4/1/0x4000000000000000 softirq=152230/152230 fqs=7492 [ 1442.379033] (t=15000 jiffies g=391269 q=378 ncpus=8) [ 1442.379038] CPU: 4 PID: 1456 Comm: glmark2-es2-way Not tainted 6.1.99 #406 [ 1442.379041] Hardware name: Rockchip RK3588 EVB7 V11 Board (DT) [ 1442.379044] pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 1442.379047] pc : mas_empty_area_rev+0x150/0x540 [ 1442.379054] lr : mas_empty_area_rev+0x198/0x540 [ 1442.379056] sp : ffffffc00f6ebae0 ... [ 1442.379097] Call trace: [ 1442.379099] mas_empty_area_rev+0x150/0x540 [ 1442.379102] kbase_unmapped_area_topdown+0x144/0x264 [ 1442.379107] kbase_context_get_unmapped_area+0x244/0x2ec [ 1442.379111] kbase_get_unmapped_area+0x48/0x70 [ 1442.379116] get_unmapped_area+0x5c/0x100 [ 1442.379121] do_mmap+0xe0/0x450 [ 1442.379124] vm_mmap_pgoff+0xa0/0x150 [ 1442.379128] ksys_mmap_pgoff+0x9c/0xd0 [ 1442.379132] __arm64_sys_mmap+0x34/0x44 [ 1442.379136] invoke_syscall+0x4c/0x114 [ 1442.379141] el0_svc_common.constprop.0+0x54/0x180 [ 1442.379144] do_el0_svc+0x20/0x2c [ 1442.379148] el0_svc+0x14/0x80 [ 1442.379151] el0t_64_sync_handler+0xb0/0xb4 [ 1442.379155] el0t_64_sync+0x158/0x15c [ 1442.379158] [ 1442.379158] PC: 0xffffffc009375950: ... Change-Id: I6c5b1d37cf7f0853282126c23ce779032b7fabe7 Signed-off-by: Zhen Chen --- .../arm/bifrost/thirdparty/mali_kbase_mmap.c | 344 +++++++++--------- 1 file changed, 167 insertions(+), 177 deletions(-) diff --git a/drivers/gpu/arm/bifrost/thirdparty/mali_kbase_mmap.c b/drivers/gpu/arm/bifrost/thirdparty/mali_kbase_mmap.c index cfb347affa2e..97df69ede4b5 100644 --- a/drivers/gpu/arm/bifrost/thirdparty/mali_kbase_mmap.c +++ b/drivers/gpu/arm/bifrost/thirdparty/mali_kbase_mmap.c @@ -20,155 +20,88 @@ * kbase_context_get_unmapped_area() interface. */ -#if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE) /** - * move_mt_gap() - Search the maple tree for an existing gap of a particular size - * immediately before another pre-identified gap. - * @gap_start: Pre-identified gap starting address. - * @gap_end: Pre-identified gap ending address. - * @size: Size of the new gap needed before gap_start. + * shader_code_align_and_check() - Align the specified pointer according to shader code + * requirement. * - * This function will search the calling process' maple tree - * for another gap, one that is immediately preceding the pre-identified - * gap, for a specific size, and upon success it will decrement gap_end - * by the specified size, and replace gap_start with the new gap_start of - * the newly identified gap. + * @gap_end: Highest possible start address for alignment. The caller must ensure + * the input has already been properly aligned with info contained fields. + * @info: vm_unmapped_area_info structure passed, containing alignment, length + * and limits for the allocation + * The function only undertakes the shader code alignment adjustment. It's the caller's + * responsibility that the input value provided via gap_end has already been properly aligned + * in compliance to the fields specified in the info structure. Irrespective the return result, + * the value of the variable pointed by the pointer gap_end may have been decreased in + * reaching the required alignment, but will not drop below info->low_limit. * - * Return: true if large enough preceding gap is found, false otherwise. + * Return: true if gap_end is now aligned correctly, false otherwise */ -static bool move_mt_gap(unsigned long *gap_start, unsigned long *gap_end, unsigned long size) +static bool shader_code_align_and_check(unsigned long *gap_end, struct vm_unmapped_area_info *info) { - unsigned long new_gap_start, new_gap_end; + unsigned long align_adjust = (info->align_offset ? info->align_offset : info->length); + unsigned long align_floor = info->low_limit + align_adjust; - MA_STATE(mas, ¤t->mm->mm_mt, 0, 0); + /* Check for 4GB address inner high-bit pattern, make adjustment if all zeros */ + if (0 == (*gap_end & BASE_MEM_MASK_4GB) && *gap_end >= align_floor) + (*gap_end) -= align_adjust; + if (0 == ((*gap_end + info->length) & BASE_MEM_MASK_4GB) && *gap_end >= align_floor) + (*gap_end) -= align_adjust; - if (*gap_end < size) - return false; - - /* Calculate the gap end for the new, resultant gap */ - new_gap_end = *gap_end - size; - - /* If the new gap_end (i.e. new VA start address) is larger than gap_start, than the - * pre-identified gap already has space to shrink to accommodate the decrease in - * gap_end. - */ - if (new_gap_end >= *gap_start) { - /* Pre-identified gap already has space - just patch gap_end to new - * lower value and exit. - */ - *gap_end = new_gap_end; - return true; - } - - /* Since the new VA start address (new_gap_end) is below the start of the pre-identified - * gap in the maple tree, see if there is a free gap directly before the existing gap, of - * the same size as the alignment shift, such that the effective gap found is "extended". - * This may be larger than needed but leaves the same distance between gap_end and gap_start - * that currently exists. - */ - new_gap_start = *gap_start - size; - if (mas_empty_area_rev(&mas, new_gap_start, *gap_start - 1, size)) { - /* There's no gap between the new start address needed and the - * current start address - so return false to find a new - * gap from the maple tree. - */ - return false; - } - /* Suitable gap found - replace gap_start and gap_end with new values. gap_start takes the - * value of the start of new gap found, which now correctly precedes gap_end, and gap_end - * takes on the new aligned value that has now been decremented by the requested size. - */ - *gap_start = mas.index; - *gap_end = new_gap_end; - return true; + return ((*gap_end & BASE_MEM_MASK_4GB) && ((*gap_end + info->length) & BASE_MEM_MASK_4GB)); } /** - * align_and_check() - Align the specified pointer to the provided alignment and - * check that it is still in range. On kernel 6.1 onwards - * this function does not require that the initial requested - * gap is extended with the maximum size needed to guarantee - * an alignment. - * @gap_end: Highest possible start address for allocation (end of gap in - * address space) - * @gap_start: Start address of current memory area / gap in address space - * @info: vm_unmapped_area_info structure passed to caller, containing - * alignment, length and limits for the allocation - * @is_shader_code: True if the allocation is for shader code (which has - * additional alignment requirements) - * @is_same_4gb_page: True if the allocation needs to reside completely within - * a 4GB chunk + * align_4gb_no_straddle() - Align the specified pointer not to straddle over a 4_GB boundary. * - * Return: true if gap_end is now aligned correctly and is still in range, - * false otherwise + * @gap_end: Highest possible start address for alignment. The caller must ensure + * the input has already been properly aligned with info contained fields. + * @info: vm_unmapped_area_info structure passed, containing alignment, length + * and limits for the allocation + * + * The function only undertakes the 4GB boundary alignment adjustment. It's the caller's + * responsibility that the input value provided via gap_end has already been properly aligned + * in compliance to the fields specified in the info structure. + * + * Return: true is always expected and the gap_end is aligned correctly, false can only + * be possible when the code has been wrongly modified. */ -static bool align_and_check(unsigned long *gap_end, unsigned long gap_start, - struct vm_unmapped_area_info *info, bool is_shader_code, - bool is_same_4gb_page) +static bool align_4gb_no_straddle(unsigned long *gap_end, struct vm_unmapped_area_info *info) { - unsigned long alignment_shift; + unsigned long start = *gap_end; + unsigned long end = *gap_end + info->length; + unsigned long mask = ~((unsigned long)U32_MAX); - /* Compute highest gap address at the desired alignment */ - *gap_end -= info->length; - alignment_shift = (*gap_end - info->align_offset) & info->align_mask; + /* Check if 4GB boundary is straddled */ + if ((start & mask) != ((end - 1) & mask)) { + unsigned long offset = end - (end & mask); + /* This is to ensure that alignment doesn't get + * disturbed in an attempt to prevent straddling at + * 4GB boundary. The GPU VA is aligned to 2MB when the + * allocation size is > 2MB and there is enough CPU & + * GPU virtual space. + */ + unsigned long rounded_offset = ALIGN(offset, info->align_mask + 1); - /* Align desired start VA (gap_end) by calculated alignment shift amount */ - if (!move_mt_gap(&gap_start, gap_end, alignment_shift)) - return false; - /* Alignment is done so far - check for further alignment requirements */ + start -= rounded_offset; + end -= rounded_offset; - if (is_shader_code) { - /* Shader code allocations must not start or end on a 4GB boundary */ - alignment_shift = info->align_offset ? info->align_offset : info->length; - if (0 == (*gap_end & BASE_MEM_MASK_4GB)) { - if (!move_mt_gap(&gap_start, gap_end, alignment_shift)) - return false; - } - if (0 == ((*gap_end + info->length) & BASE_MEM_MASK_4GB)) { - if (!move_mt_gap(&gap_start, gap_end, alignment_shift)) - return false; - } + /* Patch gap_end to use new starting address for VA region */ + *gap_end = start; - if (!(*gap_end & BASE_MEM_MASK_4GB) || - !((*gap_end + info->length) & BASE_MEM_MASK_4GB)) + /* The preceding 4GB boundary shall not get straddled, + * even after accounting for the alignment, as the + * size of allocation is limited to 4GB and the initial + * start location was already aligned. + */ + if (WARN_ONCE((start & mask) != ((end - 1) & mask), + "Alignment unexpected straddles over 4GB boundary!")) return false; - } else if (is_same_4gb_page) { - unsigned long start = *gap_end; - unsigned long end = *gap_end + info->length; - unsigned long mask = ~((unsigned long)U32_MAX); - - /* Check if 4GB boundary is straddled */ - if ((start & mask) != ((end - 1) & mask)) { - unsigned long offset = end - (end & mask); - /* This is to ensure that alignment doesn't get - * disturbed in an attempt to prevent straddling at - * 4GB boundary. The GPU VA is aligned to 2MB when the - * allocation size is > 2MB and there is enough CPU & - * GPU virtual space. - */ - unsigned long rounded_offset = ALIGN(offset, info->align_mask + 1); - - if (!move_mt_gap(&gap_start, gap_end, rounded_offset)) - return false; - /* Re-calculate start and end values */ - start = *gap_end; - end = *gap_end + info->length; - - /* The preceding 4GB boundary shall not get straddled, - * even after accounting for the alignment, as the - * size of allocation is limited to 4GB and the initial - * start location was already aligned. - */ - WARN_ON((start & mask) != ((end - 1) & mask)); - } } - if ((*gap_end < info->low_limit) || (*gap_end < gap_start)) - return false; - return true; } -#else + +#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE) || !defined(__ANDROID_COMMON_KERNEL__) /** * align_and_check() - Align the specified pointer to the provided alignment and * check that it is still in range. For Kernel versions below @@ -196,45 +129,11 @@ static bool align_and_check(unsigned long *gap_end, unsigned long gap_start, *gap_end -= (*gap_end - info->align_offset) & info->align_mask; if (is_shader_code) { - /* Check for 4GB boundary */ - if (0 == (*gap_end & BASE_MEM_MASK_4GB)) - (*gap_end) -= (info->align_offset ? info->align_offset : info->length); - if (0 == ((*gap_end + info->length) & BASE_MEM_MASK_4GB)) - (*gap_end) -= (info->align_offset ? info->align_offset : info->length); - - if (!(*gap_end & BASE_MEM_MASK_4GB) || - !((*gap_end + info->length) & BASE_MEM_MASK_4GB)) + if (!shader_code_align_and_check(gap_end, info)) + return false; + } else if (is_same_4gb_page) + if (!align_4gb_no_straddle(gap_end, info)) return false; - } else if (is_same_4gb_page) { - unsigned long start = *gap_end; - unsigned long end = *gap_end + info->length; - unsigned long mask = ~((unsigned long)U32_MAX); - - /* Check if 4GB boundary is straddled */ - if ((start & mask) != ((end - 1) & mask)) { - unsigned long offset = end - (end & mask); - /* This is to ensure that alignment doesn't get - * disturbed in an attempt to prevent straddling at - * 4GB boundary. The GPU VA is aligned to 2MB when the - * allocation size is > 2MB and there is enough CPU & - * GPU virtual space. - */ - unsigned long rounded_offset = ALIGN(offset, info->align_mask + 1); - - start -= rounded_offset; - end -= rounded_offset; - - /* Patch gap_end to use new starting address for VA region */ - *gap_end = start; - - /* The preceding 4GB boundary shall not get straddled, - * even after accounting for the alignment, as the - * size of allocation is limited to 4GB and the initial - * start location was already aligned. - */ - WARN_ON((start & mask) != ((end - 1) & mask)); - } - } if ((*gap_end < info->low_limit) || (*gap_end < gap_start)) return false; @@ -370,33 +269,124 @@ check_current: } } } -#else - unsigned long high_limit, gap_start, gap_end; +#else /* KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE */ +#ifdef __ANDROID_COMMON_KERNEL__ + struct vm_unmapped_area_info tmp_info = *info; + unsigned long length; + + tmp_info.flags |= VM_UNMAPPED_AREA_TOPDOWN; + if (!(is_shader_code || is_same_4gb_page)) + return vm_unmapped_area(&tmp_info); + + length = info->length + info->align_mask; + + /* Due to additional alignment requirement, shader_code or same_4gb_page + * needs iterations for alignment search and confirmation check. + */ + while (true) { + unsigned long saved_high_lmt = tmp_info.high_limit; + unsigned long gap_end, start, rev_high_limit; + + gap_end = vm_unmapped_area(&tmp_info); + if (IS_ERR_VALUE(gap_end)) + return gap_end; + + start = gap_end; + if (is_shader_code) { + bool shader_code_aligned; + unsigned long align_cmp_ref; + + while (true) { + /* Save the start value for progress check. the loop needs + * to end if the alignment can't progress any further. + * In summary, the loop ends condition here is either: + * 1. shader_code_aligned is true; or + * 2. align_cmp_ref == gap_end. + */ + align_cmp_ref = gap_end; + + shader_code_aligned = + shader_code_align_and_check(&gap_end, &tmp_info); + if (shader_code_aligned || (align_cmp_ref == gap_end)) + break; + } + + if (shader_code_aligned) { + if (start == gap_end) + return gap_end; + + rev_high_limit = gap_end + length; + } else + break; + } else { + /* must be same_4gb_page case */ + if (likely(align_4gb_no_straddle(&gap_end, &tmp_info))) { + if (start == gap_end) + return gap_end; + + rev_high_limit = gap_end + length; + } else + break; + } + + if (rev_high_limit < info->low_limit) + break; + + if (WARN_ONCE(rev_high_limit >= saved_high_lmt, + "Unexpected recurring high_limit in search, %lx => %lx\n" + "\tinfo-input: limit=[%lx, %lx], mask=%lx, len=%lx\n", + saved_high_lmt, rev_high_limit, info->low_limit, info->high_limit, + info->align_mask, info->length)) + rev_high_limit = saved_high_lmt - + (info->align_offset ? info->align_offset : info->length); + + /* Repeat the search with a decreasing rev_high_limit */ + tmp_info.high_limit = rev_high_limit; + } +#else /* __ANDROID_COMMON_KERNEL__ */ + unsigned long length, high_limit; MA_STATE(mas, ¤t->mm->mm_mt, 0, 0); - /* - * Adjust search limits by the desired length. - * See implementation comment at top of unmapped_area(). - */ - gap_end = info->high_limit; - if (gap_end < info->length) + /* Adjust search length to account for worst case alignment overhead */ + length = info->length + info->align_mask; + if (length < info->length) return -ENOMEM; - high_limit = gap_end - info->length; - if (info->low_limit > high_limit) + high_limit = info->high_limit; + if ((high_limit - info->low_limit) < length) return -ENOMEM; while (true) { - if (mas_empty_area_rev(&mas, info->low_limit, info->high_limit - 1, info->length)) + unsigned long gap_start, gap_end; + unsigned long saved_high_lmt = high_limit; + + if (mas_empty_area_rev(&mas, info->low_limit, high_limit - 1, length)) return -ENOMEM; + gap_end = mas.last + 1; gap_start = mas.index; if (align_and_check(&gap_end, gap_start, info, is_shader_code, is_same_4gb_page)) return gap_end; + + if (gap_end < info->low_limit) + return -ENOMEM; + + /* Adjust next search high limit */ + high_limit = gap_end + length; + + if (WARN_ONCE(high_limit >= saved_high_lmt, + "Unexpected recurring high_limit in search, %lx => %lx\n" + "\tinfo-input: limit=[%lx, %lx], mask=%lx, len=%lx\n", + saved_high_lmt, high_limit, info->low_limit, info->high_limit, + info->align_mask, info->length)) + high_limit = saved_high_lmt - + (info->align_offset ? info->align_offset : info->length); + mas_reset(&mas); } -#endif +#endif /* __ANDROID_COMMON_KERNEL__ */ +#endif /* KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE */ return -ENOMEM; } From b1541119af2f21b62ad27a9dd6b235419c7b77f5 Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Fri, 30 Aug 2024 14:52:00 +0800 Subject: [PATCH 3/4] media: i2c: rk628: fix mipi csitx enable sequence 1.add data reset after video enable 2.fix mipi dphy reset before dphy init Change-Id: I003f1c99563dc658f4bbff706de84668e24161d2 Signed-off-by: Jianwei Fan --- drivers/media/i2c/rk628/rk628_csi_v4l2.c | 65 +++++++++++++++-------- drivers/media/i2c/rk628/rk628_mipi_dphy.c | 22 ++++++-- 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/drivers/media/i2c/rk628/rk628_csi_v4l2.c index 973c7f769e33..ee9ee734d34b 100644 --- a/drivers/media/i2c/rk628/rk628_csi_v4l2.c +++ b/drivers/media/i2c/rk628/rk628_csi_v4l2.c @@ -755,7 +755,17 @@ static void rk628_csi_soft_reset(struct v4l2_subdev *sd) static void enable_csitx(struct v4l2_subdev *sd) { struct rk628_csi *csi = to_csi(sd); + u32 mask = SW_OUTPUT_MODE_MASK; + u32 val = SW_OUTPUT_MODE(OUTPUT_MODE_CSI); + if (csi->rk628->version == RK628F_VERSION) { + mask = SW_OUTPUT_COMBTX_MODE_MASK; + val = SW_OUTPUT_COMBTX_MODE(OUTPUT_MODE_CSI - 1); + rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON3, + GRF_AS_DSIPHY_MASK, + GRF_AS_DSIPHY(0)); + } + rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON0, mask, val); //enable dphy1 and split mode rk628_i2c_update_bits(csi->rk628, GRF_SYSTEM_CON3, GRF_DPHY_CH1_EN_MASK, csi->rk628->dual_mipi ? GRF_DPHY_CH1_EN(1) : 0); @@ -912,6 +922,7 @@ static void rk628_csi_disable_stream(struct v4l2_subdev *sd) csi->continues_clk ? CONT_MODE_CLK_CLR(1) : CONT_MODE_CLK_CLR(0)); rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD); } + rk628_csi_soft_reset(sd); mipi_dphy_power_off(csi); csi->txphy_pwron = false; csi->is_streaming = false; @@ -935,14 +946,17 @@ static void enable_stream(struct v4l2_subdev *sd, bool en) return; } - if (csi->plat_data->tx_mode == DSI_MODE) { + if (csi->plat_data->tx_mode == DSI_MODE) enable_dsitx(sd); - } else { + else enable_csitx(sd); - /* csitx int en */ + + rk628_hdmirx_vid_enable(sd, true); + if (csi->plat_data->tx_mode == CSI_MODE) { + msleep(20); + rk628_mipi_txdata_reset(sd); rk628_csi_enable_csi_interrupts(sd, true); } - rk628_hdmirx_vid_enable(sd, true); rk628_i2c_update_bits(csi->rk628, HDMI_RX_PDEC_CTRL, GCPFORCE_CLRAVMUTE_MASK, GCPFORCE_CLRAVMUTE(1)); @@ -1681,6 +1695,7 @@ static void rk628_csi_error_process(struct v4l2_subdev *sd) #if IS_REACHABLE(CONFIG_VIDEO_ROCKCHIP_CIF) rk628_csi_reset_rkcif(sd); #endif + rk628_i2c_update_bits(csi->rk628, CSITX_CSITX_EN, CSITX_EN_MASK, CSITX_EN(1)); rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD); if (csi->rk628->version >= RK628F_VERSION) { @@ -1710,8 +1725,10 @@ static void rk628_csi_error_process(struct v4l2_subdev *sd) CSITX1_CONFIG_DONE, CONFIG_DONE_IMD); } } - rk628_csi_enable_csi_interrupts(sd, true); rk628_hdmirx_vid_enable(sd, true); + msleep(20); + rk628_mipi_txdata_reset(sd); + rk628_csi_enable_csi_interrupts(sd, true); v4l2_dbg(1, debug, sd, "%s, do reset successful\n", __func__); } else { v4l2_info(sd, @@ -2465,12 +2482,16 @@ static void rk628_csi_reset_streaming(struct v4l2_subdev *sd, int on) DPHY_EN_MASK | CSITX_EN_MASK, DPHY_EN(1) | CSITX_EN(1)); rk628_i2c_write(csi->rk628, CSITX1_CONFIG_DONE, CONFIG_DONE_IMD); } - rk628_csi_enable_csi_interrupts(sd, true); - csi->is_streaming = true; } else { enable_dsitx(sd); } rk628_hdmirx_vid_enable(sd, true); + if (csi->plat_data->tx_mode == CSI_MODE) { + msleep(20); + rk628_mipi_txdata_reset(sd); + rk628_csi_enable_csi_interrupts(sd, true); + csi->is_streaming = true; + } } else { rk628_hdmirx_vid_enable(sd, false); if (csi->plat_data->tx_mode == CSI_MODE) { @@ -2644,10 +2665,12 @@ static int mipi_dphy_power_on(struct rk628_csi *csi) bus_width |= COMBTXPHY_MODULEB_EN; v4l2_dbg(1, debug, sd, "%s mipi bitrate:%llu mbps\n", __func__, csi->lane_mbps); + rk628_mipi_dphy_reset_assert(csi->rk628); + rk628_mipi_dphy_reset_deassert(csi->rk628); + rk628_txphy_set_bus_width(csi->rk628, bus_width); rk628_txphy_set_mode(csi->rk628, PHY_MODE_VIDEO_MIPI); - rk628_mipi_dphy_reset_assert(csi->rk628); rk628_mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps, 0); if (csi->rk628->version >= RK628F_VERSION) rk628_mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps, 1); @@ -2664,25 +2687,23 @@ static int mipi_dphy_power_on(struct rk628_csi *csi) if (csi->rk628->version >= RK628F_VERSION) rk628_mipi_dphy_init_hsmanual(csi->rk628, false, 1); } - rk628_mipi_dphy_reset_deassert(csi->rk628); - usleep_range(1500, 2000); rk628_txphy_power_on(csi->rk628); usleep_range(1500, 2000); - mask = DPHY_PLL_LOCK; - rk628_i2c_read(csi->rk628, CSITX_CSITX_STATUS1, &val); - if ((val & mask) != mask) { - dev_err(csi->dev, "PHY is not locked\n"); - return -1; - } + ret = regmap_read_poll_timeout(csi->rk628->regmap[RK628_DEV_CSI], + CSITX_CSITX_STATUS1, + val, val & DPHY_PLL_LOCK, + 0, 1000); + if (ret < 0) + dev_err(csi->rk628->dev, "csi0 phy is not locked\n"); if (csi->rk628->version >= RK628F_VERSION) { - rk628_i2c_read(csi->rk628, CSITX1_CSITX_STATUS1, &val); - if ((val & mask) != mask) { - dev_err(csi->dev, "PHY1 is not locked\n"); - return -1; - } + ret = regmap_read_poll_timeout(csi->rk628->regmap[RK628_DEV_CSI1], + CSITX1_CSITX_STATUS1, + val, val & DPHY_PLL_LOCK, + 0, 1000); + if (ret < 0) + dev_err(csi->rk628->dev, "csi1 phy is not locked\n"); } - udelay(10); mask = STOPSTATE_CLK | STOPSTATE_LANE0; ret = regmap_read_poll_timeout(csi->rk628->regmap[RK628_DEV_CSI], diff --git a/drivers/media/i2c/rk628/rk628_mipi_dphy.c b/drivers/media/i2c/rk628/rk628_mipi_dphy.c index 5b53a6028ebf..2cfcda0d47e8 100644 --- a/drivers/media/i2c/rk628/rk628_mipi_dphy.c +++ b/drivers/media/i2c/rk628/rk628_mipi_dphy.c @@ -130,6 +130,20 @@ static inline void mipi_dphy_enablelane_deassert(struct rk628 *rk628, uint8_t mi udelay(1); } +static inline void mipi_dphy_enableclk_assert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? CSITX1_DPHY_CTRL : CSITX_DPHY_CTRL, + DPHY_ENABLECLK, DPHY_ENABLECLK); + udelay(1); +} + +static inline void mipi_dphy_enableclk_deassert(struct rk628 *rk628, uint8_t mipi_id) +{ + rk628_i2c_update_bits(rk628, mipi_id ? CSITX1_DPHY_CTRL : CSITX_DPHY_CTRL, + DPHY_ENABLECLK, 0); + udelay(1); +} + static inline void mipi_dphy_shutdownz_assert(struct rk628 *rk628) { rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON, CSI_PHYSHUTDOWNZ, 0); @@ -227,9 +241,9 @@ int rk628_mipi_dphy_reset_assert(struct rk628 *rk628) rk628_i2c_write(rk628, CSITX_SYS_CTRL0_IMD, 0x1); if (rk628->version >= RK628F_VERSION) rk628_i2c_write(rk628, CSITX1_SYS_CTRL0_IMD, 0x1); - mipi_dphy_enablelane_deassert(rk628, 0); + mipi_dphy_enableclk_deassert(rk628, 0); if (rk628->version >= RK628F_VERSION) - mipi_dphy_enablelane_deassert(rk628, 1); + mipi_dphy_enableclk_deassert(rk628, 1); mipi_dphy_shutdownz_assert(rk628); mipi_dphy_rstz_assert(rk628); rk628_testif_testclr_assert(rk628, 0); @@ -248,9 +262,9 @@ int rk628_mipi_dphy_reset_assert(struct rk628 *rk628) rk628_testif_testclr_deassert(rk628, 0); if (rk628->version >= RK628F_VERSION) rk628_testif_testclr_deassert(rk628, 1); - mipi_dphy_enablelane_assert(rk628, 0); + mipi_dphy_enableclk_assert(rk628, 0); if (rk628->version >= RK628F_VERSION) - mipi_dphy_enablelane_assert(rk628, 1); + mipi_dphy_enableclk_assert(rk628, 1); return 0; } From 72caa4cae073e4ec21db749a613659e10660ecac Mon Sep 17 00:00:00 2001 From: Jianwei Fan Date: Fri, 8 Oct 2021 08:26:32 +0000 Subject: [PATCH 4/4] media: i2c: ov8856: sync driver from kernel4.19 Signed-off-by: Jianwei Fan Signed-off-by: Wang Panzhenzhuan Change-Id: Ica77b8fbd62bae43ba16fec968b685e77d48e06e --- drivers/media/i2c/ov8856.c | 4204 ++++++++++++++++-------------------- 1 file changed, 1889 insertions(+), 2315 deletions(-) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index aaf42ece0a11..f5a3e89d6a59 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -1,1397 +1,789 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2019 Intel Corporation. +/* + * ov8856 camera driver + * Copyright (C) 2021 Rockchip Electronics Co., Ltd. + * + * v0.1.0x00 : first driver version. + * V0.0X01.0X01 fix bus format. + * V0.0X01.0X02 support mirror & flip ctrl. + * + */ -#include -#include #include +#include #include #include #include #include #include +#include +#include #include +#include +#include +#include +#include +#include + +#include +#include +#include #include #include +#include #include +#include +#include +#include +#include "otp_eeprom.h" + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x02) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif +/*MIPI_FREQ * 2 * lane / bit*/ +#define OV8856_PIXEL_RATE (360000000LL * 2LL * 4LL / 10LL) + +#define MIPI_FREQ 360000000U +#define OV8856_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x00885a +#define OV8856_REG_CHIP_ID 0x300a + +#define OV8856_REG_CTRL_MODE 0x0100 +#define OV8856_MODE_SW_STANDBY 0x0 +#define OV8856_MODE_STREAMING 0x1 + +#define OV8856_REG_EXPOSURE 0x3500 +#define OV8856_EXPOSURE_MIN 6 +#define OV8856_EXPOSURE_STEP 1 +#define OV8856_VTS_MAX 0x7fff + +#define OV8856_REG_GAIN_H 0x3508 +#define OV8856_REG_GAIN_L 0x3509 +#define OV8856_GAIN_H_MASK 0x1f +#define OV8856_GAIN_H_SHIFT 8 +#define OV8856_GAIN_L_MASK 0xff +#define OV8856_GAIN_MIN 0x80 +#define OV8856_GAIN_MAX 0x7c0 +#define OV8856_GAIN_STEP 1 +#define OV8856_GAIN_DEFAULT 0x80 + +#define OV8856_REG_TEST_PATTERN 0x5e00 +#define OV8856_TEST_PATTERN_ENABLE 0x80 +#define OV8856_TEST_PATTERN_DISABLE 0x0 + +#define OV8856_REG_VTS 0x380e + +#define OV8856_MIRROR_REG 0x3820 +#define OV8856_FLIP_REG 0x3821 +#define MIRROR_BIT_MASK 0x06 +#define FLIP_BIT_MASK 0x06 + +#define OV8856_ISPCTRL1_REG 0x5001 +#define OV8856_ISPCTRL2E_REG 0x502e + +#define ISPCTRL1_BIT_MASK BIT(2) +#define ISPCTRL2E_BIT_MASK (BIT(1)|BIT(0)) + +#define REG_NULL 0xFFFF #define OV8856_REG_VALUE_08BIT 1 #define OV8856_REG_VALUE_16BIT 2 #define OV8856_REG_VALUE_24BIT 3 -#define OV8856_SCLK 144000000ULL -#define OV8856_XVCLK_19_2 19200000 -#define OV8856_DATA_LANES 4 -#define OV8856_RGB_DEPTH 10 +#define OV8856_LANES 4 +#define OV8856_BITS_PER_SAMPLE 10 -#define OV8856_REG_CHIP_ID 0x300a -#define OV8856_CHIP_ID 0x00885a +#define OV8856_CHIP_REVISION_REG 0x302A -#define OV8856_REG_MODE_SELECT 0x0100 -#define OV8856_MODE_STANDBY 0x00 -#define OV8856_MODE_STREAMING 0x01 +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" -/* module revisions */ -#define OV8856_2A_MODULE 0x01 -#define OV8856_1B_MODULE 0x02 - -/* the OTP read-out buffer is at 0x7000 and 0xf is the offset - * of the byte in the OTP that means the module revision - */ -#define OV8856_MODULE_REVISION 0x700f -#define OV8856_OTP_MODE_CTRL 0x3d84 -#define OV8856_OTP_LOAD_CTRL 0x3d81 -#define OV8856_OTP_MODE_AUTO 0x00 -#define OV8856_OTP_LOAD_CTRL_ENABLE BIT(0) - -/* vertical-timings from sensor */ -#define OV8856_REG_VTS 0x380e -#define OV8856_VTS_MAX 0x7fff - -/* horizontal-timings from sensor */ -#define OV8856_REG_HTS 0x380c - -/* Exposure controls from sensor */ -#define OV8856_REG_EXPOSURE 0x3500 -#define OV8856_EXPOSURE_MIN 6 -#define OV8856_EXPOSURE_MAX_MARGIN 6 -#define OV8856_EXPOSURE_STEP 1 - -/* Analog gain controls from sensor */ -#define OV8856_REG_ANALOG_GAIN 0x3508 -#define OV8856_ANAL_GAIN_MIN 128 -#define OV8856_ANAL_GAIN_MAX 2047 -#define OV8856_ANAL_GAIN_STEP 1 - -/* Digital gain controls from sensor */ -#define OV8856_REG_DIGITAL_GAIN 0x350a -#define OV8856_REG_MWB_R_GAIN 0x5019 -#define OV8856_REG_MWB_G_GAIN 0x501b -#define OV8856_REG_MWB_B_GAIN 0x501d -#define OV8856_DGTL_GAIN_MIN 0 -#define OV8856_DGTL_GAIN_MAX 4095 -#define OV8856_DGTL_GAIN_STEP 1 -#define OV8856_DGTL_GAIN_DEFAULT 1024 - -/* Test Pattern Control */ -#define OV8856_REG_TEST_PATTERN 0x5e00 -#define OV8856_TEST_PATTERN_ENABLE BIT(7) -#define OV8856_TEST_PATTERN_BAR_SHIFT 2 - -#define NUM_REGS 7 -#define NUM_MODE_REGS 187 -#define NUM_MODE_REGS_2 200 - -/* Flip Mirror Controls from sensor */ -#define OV8856_REG_FORMAT1 0x3820 -#define OV8856_REG_FORMAT2 0x3821 -#define OV8856_REG_FORMAT1_OP_1 BIT(1) -#define OV8856_REG_FORMAT1_OP_2 BIT(2) -#define OV8856_REG_FORMAT1_OP_3 BIT(6) -#define OV8856_REG_FORMAT2_OP_1 BIT(1) -#define OV8856_REG_FORMAT2_OP_2 BIT(2) -#define OV8856_REG_FORMAT2_OP_3 BIT(6) -#define OV8856_REG_FLIP_OPT_1 0x376b -#define OV8856_REG_FLIP_OPT_2 0x5001 -#define OV8856_REG_FLIP_OPT_3 0x502e -#define OV8856_REG_MIRROR_OPT_1 0x5004 -#define OV8856_REG_FLIP_OP_0 BIT(0) -#define OV8856_REG_FLIP_OP_1 BIT(1) -#define OV8856_REG_FLIP_OP_2 BIT(2) -#define OV8856_REG_MIRROR_OP_1 BIT(1) -#define OV8856_REG_MIRROR_OP_2 BIT(2) - -#define to_ov8856(_sd) container_of(_sd, struct ov8856, sd) +#define OV8856_NAME "ov8856" +#define OV8856_MEDIA_BUS_FMT MEDIA_BUS_FMT_SBGGR10_1X10 +#define MIRROR 1 +static const struct regval *ov8856_global_regs; static const char * const ov8856_supply_names[] = { - "dovdd", /* Digital I/O power */ "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ "dvdd", /* Digital core power */ }; -enum { - OV8856_MEDIA_BUS_FMT_SBGGR10_1X10, - OV8856_MEDIA_BUS_FMT_SGRBG10_1X10, -}; +#define OV8856_NUM_SUPPLIES ARRAY_SIZE(ov8856_supply_names) -struct ov8856_reg { - u16 address; +struct regval { + u16 addr; u8 val; }; -struct ov8856_reg_list { - u32 num_of_regs; - const struct ov8856_reg *regs; -}; - -struct ov8856_link_freq_config { - const struct ov8856_reg_list reg_list; -}; - struct ov8856_mode { - /* Frame width in pixels */ u32 width; - - /* Frame height in pixels */ u32 height; - - /* Horizontal timining size */ - u32 hts; - - /* Default vertical timining size */ + struct v4l2_fract max_fps; + u32 hts_def; u32 vts_def; - - /* Min vertical timining size */ - u32 vts_min; - - /* Link frequency needed for this resolution */ - u32 link_freq_index; - - /* Sensor register settings for this resolution */ - const struct ov8856_reg_list reg_list; - - /* Number of data lanes */ - u8 data_lanes; - - /* Default MEDIA_BUS_FMT for this mode */ - u32 default_mbus_index; + u32 exp_def; + const struct regval *reg_list; }; -struct ov8856_mipi_data_rates { - const struct ov8856_reg regs_0[NUM_REGS]; - const struct ov8856_reg regs_1[NUM_REGS]; +struct ov8856 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[OV8856_NUM_SUPPLIES]; + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; + + struct v4l2_subdev subdev; + struct media_pad pad; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *anal_gain; + struct v4l2_ctrl *digi_gain; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *test_pattern; + struct v4l2_ctrl *h_flip; + struct v4l2_ctrl *v_flip; + struct mutex mutex; + bool streaming; + const struct ov8856_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + bool power_on; + struct otp_info *otp; + + u32 module_index; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_inf module_inf; + struct rkmodule_awb_cfg awb_cfg; + struct rkmodule_lsc_cfg lsc_cfg; }; -static const struct ov8856_mipi_data_rates mipi_data_rate_lane_2 = { - //mipi_data_rate_1440mbps - { - {0x0103, 0x01}, - {0x0100, 0x00}, - {0x0302, 0x43}, - {0x0303, 0x00}, - {0x030b, 0x02}, - {0x030d, 0x4b}, - {0x031e, 0x0c} - }, - //mipi_data_rate_720mbps - { - {0x0103, 0x01}, - {0x0100, 0x00}, - {0x0302, 0x4b}, - {0x0303, 0x01}, - {0x030b, 0x02}, - {0x030d, 0x4b}, - {0x031e, 0x0c} - } -}; +#define to_ov8856(sd) container_of(sd, struct ov8856, subdev) -static const struct ov8856_mipi_data_rates mipi_data_rate_lane_4 = { - //mipi_data_rate_720mbps - { - {0x0103, 0x01}, - {0x0100, 0x00}, - {0x0302, 0x4b}, - {0x0303, 0x01}, - {0x030b, 0x02}, - {0x030d, 0x4b}, - {0x031e, 0x0c} - }, - //mipi_data_rate_360mbps - { - {0x0103, 0x01}, - {0x0100, 0x00}, - {0x0302, 0x4b}, - {0x0303, 0x03}, - {0x030b, 0x02}, - {0x030d, 0x4b}, - {0x031e, 0x0c} - } -}; - -static const struct ov8856_reg lane_2_mode_3280x2464[] = { - /* 3280x2464 resolution */ - {0x3000, 0x20}, - {0x3003, 0x08}, - {0x300e, 0x20}, - {0x3010, 0x00}, - {0x3015, 0x84}, - {0x3018, 0x32}, - {0x3021, 0x23}, - {0x3033, 0x24}, - {0x3500, 0x00}, - {0x3501, 0x9a}, - {0x3502, 0x20}, - {0x3503, 0x08}, - {0x3505, 0x83}, - {0x3508, 0x01}, - {0x3509, 0x80}, - {0x350c, 0x00}, - {0x350d, 0x80}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3600, 0x72}, - {0x3601, 0x40}, - {0x3602, 0x30}, - {0x3610, 0xc5}, - {0x3611, 0x58}, - {0x3612, 0x5c}, - {0x3613, 0xca}, - {0x3614, 0x50}, - {0x3628, 0xff}, - {0x3629, 0xff}, - {0x362a, 0xff}, - {0x3633, 0x10}, - {0x3634, 0x10}, - {0x3635, 0x10}, - {0x3636, 0x10}, - {0x3663, 0x08}, - {0x3669, 0x34}, - {0x366e, 0x10}, - {0x3706, 0x86}, - {0x370b, 0x7e}, - {0x3714, 0x23}, - {0x3730, 0x12}, - {0x3733, 0x10}, - {0x3764, 0x00}, - {0x3765, 0x00}, - {0x3769, 0x62}, - {0x376a, 0x2a}, - {0x376b, 0x30}, - {0x3780, 0x00}, - {0x3781, 0x24}, - {0x3782, 0x00}, - {0x3783, 0x23}, - {0x3798, 0x2f}, - {0x37a1, 0x60}, - {0x37a8, 0x6a}, - {0x37ab, 0x3f}, - {0x37c2, 0x04}, - {0x37c3, 0xf1}, - {0x37c9, 0x80}, - {0x37cb, 0x16}, - {0x37cc, 0x16}, - {0x37cd, 0x16}, - {0x37ce, 0x16}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x06}, - {0x3804, 0x0c}, - {0x3805, 0xdf}, - {0x3806, 0x09}, - {0x3807, 0xa7}, - {0x3808, 0x0c}, - {0x3809, 0xd0}, - {0x380a, 0x09}, - {0x380b, 0xa0}, - {0x380c, 0x07}, - {0x380d, 0x88}, - {0x380e, 0x09}, - {0x380f, 0xb8}, - {0x3810, 0x00}, - {0x3811, 0x00}, - {0x3812, 0x00}, - {0x3813, 0x01}, - {0x3814, 0x01}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x80}, - {0x3821, 0x46}, - {0x382a, 0x01}, - {0x382b, 0x01}, - {0x3830, 0x06}, - {0x3836, 0x02}, - {0x3837, 0x10}, - {0x3862, 0x04}, - {0x3863, 0x08}, - {0x3cc0, 0x33}, - {0x3d85, 0x14}, - {0x3d8c, 0x73}, - {0x3d8d, 0xde}, - {0x4001, 0xe0}, - {0x4003, 0x40}, - {0x4008, 0x00}, - {0x4009, 0x0b}, - {0x400a, 0x00}, - {0x400b, 0x84}, - {0x400f, 0x80}, - {0x4010, 0xf0}, - {0x4011, 0xff}, - {0x4012, 0x02}, - {0x4013, 0x01}, - {0x4014, 0x01}, - {0x4015, 0x01}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4048, 0x00}, - {0x4049, 0x80}, - {0x4041, 0x03}, - {0x404c, 0x20}, - {0x404d, 0x00}, - {0x404e, 0x20}, - {0x4203, 0x80}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4503, 0x08}, - {0x4601, 0x80}, - {0x4800, 0x44}, - {0x4816, 0x53}, - {0x481b, 0x58}, - {0x481f, 0x27}, - {0x4837, 0x0c}, - {0x483c, 0x0f}, - {0x484b, 0x05}, - {0x5000, 0x57}, - {0x5001, 0x0a}, - {0x5004, 0x06}, - {0x502e, 0x03}, - {0x5030, 0x41}, - {0x5795, 0x02}, - {0x5796, 0x20}, - {0x5797, 0x20}, - {0x5798, 0xd5}, - {0x5799, 0xd5}, - {0x579a, 0x00}, - {0x579b, 0x50}, - {0x579c, 0x00}, - {0x579d, 0x2c}, - {0x579e, 0x0c}, - {0x579f, 0x40}, - {0x57a0, 0x09}, - {0x57a1, 0x40}, - {0x5780, 0x14}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x59f8, 0x3d}, - {0x5a08, 0x02}, - {0x5b00, 0x02}, - {0x5b01, 0x10}, - {0x5b02, 0x03}, - {0x5b03, 0xcf}, - {0x5b05, 0x6c}, - {0x5e00, 0x00} -}; - -static const struct ov8856_reg lane_2_mode_1640x1232[] = { - /* 1640x1232 resolution */ - {0x3000, 0x20}, - {0x3003, 0x08}, - {0x300e, 0x20}, - {0x3010, 0x00}, - {0x3015, 0x84}, - {0x3018, 0x32}, - {0x3021, 0x23}, - {0x3033, 0x24}, - {0x3500, 0x00}, - {0x3501, 0x4c}, - {0x3502, 0xe0}, - {0x3503, 0x08}, - {0x3505, 0x83}, - {0x3508, 0x01}, - {0x3509, 0x80}, - {0x350c, 0x00}, - {0x350d, 0x80}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3600, 0x72}, - {0x3601, 0x40}, - {0x3602, 0x30}, - {0x3610, 0xc5}, - {0x3611, 0x58}, - {0x3612, 0x5c}, - {0x3613, 0xca}, - {0x3614, 0x50}, - {0x3628, 0xff}, - {0x3629, 0xff}, - {0x362a, 0xff}, - {0x3633, 0x10}, - {0x3634, 0x10}, - {0x3635, 0x10}, - {0x3636, 0x10}, - {0x3663, 0x08}, - {0x3669, 0x34}, - {0x366e, 0x08}, - {0x3706, 0x86}, - {0x370b, 0x7e}, - {0x3714, 0x27}, - {0x3730, 0x12}, - {0x3733, 0x10}, - {0x3764, 0x00}, - {0x3765, 0x00}, - {0x3769, 0x62}, - {0x376a, 0x2a}, - {0x376b, 0x30}, - {0x3780, 0x00}, - {0x3781, 0x24}, - {0x3782, 0x00}, - {0x3783, 0x23}, - {0x3798, 0x2f}, - {0x37a1, 0x60}, - {0x37a8, 0x6a}, - {0x37ab, 0x3f}, - {0x37c2, 0x14}, - {0x37c3, 0xf1}, - {0x37c9, 0x80}, - {0x37cb, 0x16}, - {0x37cc, 0x16}, - {0x37cd, 0x16}, - {0x37ce, 0x16}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0c}, - {0x3805, 0xdf}, - {0x3806, 0x09}, - {0x3807, 0xaf}, - {0x3808, 0x06}, - {0x3809, 0x68}, - {0x380a, 0x04}, - {0x380b, 0xd0}, - {0x380c, 0x0c}, - {0x380d, 0x60}, - {0x380e, 0x05}, - {0x380f, 0xea}, - {0x3810, 0x00}, - {0x3811, 0x04}, - {0x3812, 0x00}, - {0x3813, 0x05}, - {0x3814, 0x03}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x00}, - {0x3820, 0x90}, - {0x3821, 0x67}, - {0x382a, 0x03}, - {0x382b, 0x01}, - {0x3830, 0x06}, - {0x3836, 0x02}, - {0x3837, 0x10}, - {0x3862, 0x04}, - {0x3863, 0x08}, - {0x3cc0, 0x33}, - {0x3d85, 0x14}, - {0x3d8c, 0x73}, - {0x3d8d, 0xde}, - {0x4001, 0xe0}, - {0x4003, 0x40}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x400a, 0x00}, - {0x400b, 0x84}, - {0x400f, 0x80}, - {0x4010, 0xf0}, - {0x4011, 0xff}, - {0x4012, 0x02}, - {0x4013, 0x01}, - {0x4014, 0x01}, - {0x4015, 0x01}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4048, 0x00}, - {0x4049, 0x80}, - {0x4041, 0x03}, - {0x404c, 0x20}, - {0x404d, 0x00}, - {0x404e, 0x20}, - {0x4203, 0x80}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4503, 0x08}, - {0x4601, 0x80}, - {0x4800, 0x44}, - {0x4816, 0x53}, - {0x481b, 0x58}, - {0x481f, 0x27}, - {0x4837, 0x16}, - {0x483c, 0x0f}, - {0x484b, 0x05}, - {0x5000, 0x57}, - {0x5001, 0x0a}, - {0x5004, 0x06}, - {0x502e, 0x03}, - {0x5030, 0x41}, - {0x5795, 0x00}, - {0x5796, 0x10}, - {0x5797, 0x10}, - {0x5798, 0x73}, - {0x5799, 0x73}, - {0x579a, 0x00}, - {0x579b, 0x28}, - {0x579c, 0x00}, - {0x579d, 0x16}, - {0x579e, 0x06}, - {0x579f, 0x20}, - {0x57a0, 0x04}, - {0x57a1, 0xa0}, - {0x5780, 0x14}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x59f8, 0x3d}, - {0x5a08, 0x02}, - {0x5b00, 0x02}, - {0x5b01, 0x10}, - {0x5b02, 0x03}, - {0x5b03, 0xcf}, - {0x5b05, 0x6c}, - {0x5e00, 0x00} -}; - -static const struct ov8856_reg lane_4_mode_3280x2464[] = { - /* 3280x2464 resolution */ - {0x3000, 0x20}, - {0x3003, 0x08}, - {0x300e, 0x20}, - {0x3010, 0x00}, - {0x3015, 0x84}, - {0x3018, 0x72}, - {0x3021, 0x23}, - {0x3033, 0x24}, - {0x3500, 0x00}, - {0x3501, 0x9a}, - {0x3502, 0x20}, - {0x3503, 0x08}, - {0x3505, 0x83}, - {0x3508, 0x01}, - {0x3509, 0x80}, - {0x350c, 0x00}, - {0x350d, 0x80}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3600, 0x72}, - {0x3601, 0x40}, - {0x3602, 0x30}, - {0x3610, 0xc5}, - {0x3611, 0x58}, - {0x3612, 0x5c}, - {0x3613, 0xca}, - {0x3614, 0x20}, - {0x3628, 0xff}, - {0x3629, 0xff}, - {0x362a, 0xff}, - {0x3633, 0x10}, - {0x3634, 0x10}, - {0x3635, 0x10}, - {0x3636, 0x10}, - {0x3663, 0x08}, - {0x3669, 0x34}, - {0x366e, 0x10}, - {0x3706, 0x86}, - {0x370b, 0x7e}, - {0x3714, 0x23}, - {0x3730, 0x12}, - {0x3733, 0x10}, - {0x3764, 0x00}, - {0x3765, 0x00}, - {0x3769, 0x62}, - {0x376a, 0x2a}, - {0x376b, 0x30}, - {0x3780, 0x00}, - {0x3781, 0x24}, - {0x3782, 0x00}, - {0x3783, 0x23}, - {0x3798, 0x2f}, - {0x37a1, 0x60}, - {0x37a8, 0x6a}, - {0x37ab, 0x3f}, - {0x37c2, 0x04}, - {0x37c3, 0xf1}, - {0x37c9, 0x80}, - {0x37cb, 0x16}, - {0x37cc, 0x16}, - {0x37cd, 0x16}, - {0x37ce, 0x16}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x06}, - {0x3804, 0x0c}, - {0x3805, 0xdf}, - {0x3806, 0x09}, - {0x3807, 0xa7}, - {0x3808, 0x0c}, - {0x3809, 0xd0}, - {0x380a, 0x09}, - {0x380b, 0xa0}, - {0x380c, 0x07}, - {0x380d, 0x88}, - {0x380e, 0x09}, - {0x380f, 0xb8}, - {0x3810, 0x00}, - {0x3811, 0x00}, - {0x3812, 0x00}, - {0x3813, 0x01}, - {0x3814, 0x01}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x10}, - {0x3820, 0x80}, - {0x3821, 0x46}, - {0x382a, 0x01}, - {0x382b, 0x01}, - {0x3830, 0x06}, - {0x3836, 0x02}, - {0x3862, 0x04}, - {0x3863, 0x08}, - {0x3cc0, 0x33}, - {0x3d85, 0x17}, - {0x3d8c, 0x73}, - {0x3d8d, 0xde}, - {0x4001, 0xe0}, - {0x4003, 0x40}, - {0x4008, 0x00}, - {0x4009, 0x0b}, - {0x400a, 0x00}, - {0x400b, 0x84}, - {0x400f, 0x80}, - {0x4010, 0xf0}, - {0x4011, 0xff}, - {0x4012, 0x02}, - {0x4013, 0x01}, - {0x4014, 0x01}, - {0x4015, 0x01}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4048, 0x00}, - {0x4049, 0x80}, - {0x4041, 0x03}, - {0x404c, 0x20}, - {0x404d, 0x00}, - {0x404e, 0x20}, - {0x4203, 0x80}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4503, 0x08}, - {0x4601, 0x80}, - {0x4800, 0x44}, - {0x4816, 0x53}, - {0x481b, 0x58}, - {0x481f, 0x27}, - {0x4837, 0x16}, - {0x483c, 0x0f}, - {0x484b, 0x05}, - {0x5000, 0x57}, - {0x5001, 0x0a}, - {0x5004, 0x06}, - {0x502e, 0x03}, - {0x5030, 0x41}, - {0x5780, 0x14}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x5795, 0x02}, - {0x5796, 0x20}, - {0x5797, 0x20}, - {0x5798, 0xd5}, - {0x5799, 0xd5}, - {0x579a, 0x00}, - {0x579b, 0x50}, - {0x579c, 0x00}, - {0x579d, 0x2c}, - {0x579e, 0x0c}, - {0x579f, 0x40}, - {0x57a0, 0x09}, - {0x57a1, 0x40}, - {0x59f8, 0x3d}, - {0x5a08, 0x02}, - {0x5b00, 0x02}, - {0x5b01, 0x10}, - {0x5b02, 0x03}, - {0x5b03, 0xcf}, - {0x5b05, 0x6c}, - {0x5e00, 0x00} -}; - -static const struct ov8856_reg lane_4_mode_1640x1232[] = { - /* 1640x1232 resolution */ - {0x3000, 0x20}, - {0x3003, 0x08}, - {0x300e, 0x20}, - {0x3010, 0x00}, - {0x3015, 0x84}, - {0x3018, 0x72}, - {0x3021, 0x23}, - {0x3033, 0x24}, - {0x3500, 0x00}, - {0x3501, 0x4c}, - {0x3502, 0xe0}, - {0x3503, 0x08}, - {0x3505, 0x83}, - {0x3508, 0x01}, - {0x3509, 0x80}, - {0x350c, 0x00}, - {0x350d, 0x80}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3600, 0x72}, - {0x3601, 0x40}, - {0x3602, 0x30}, - {0x3610, 0xc5}, - {0x3611, 0x58}, - {0x3612, 0x5c}, - {0x3613, 0xca}, - {0x3614, 0x20}, - {0x3628, 0xff}, - {0x3629, 0xff}, - {0x362a, 0xff}, - {0x3633, 0x10}, - {0x3634, 0x10}, - {0x3635, 0x10}, - {0x3636, 0x10}, - {0x3663, 0x08}, - {0x3669, 0x34}, - {0x366e, 0x08}, - {0x3706, 0x86}, - {0x370b, 0x7e}, - {0x3714, 0x27}, - {0x3730, 0x12}, - {0x3733, 0x10}, - {0x3764, 0x00}, - {0x3765, 0x00}, - {0x3769, 0x62}, - {0x376a, 0x2a}, - {0x376b, 0x30}, - {0x3780, 0x00}, - {0x3781, 0x24}, - {0x3782, 0x00}, - {0x3783, 0x23}, - {0x3798, 0x2f}, - {0x37a1, 0x60}, - {0x37a8, 0x6a}, - {0x37ab, 0x3f}, - {0x37c2, 0x14}, - {0x37c3, 0xf1}, - {0x37c9, 0x80}, - {0x37cb, 0x16}, - {0x37cc, 0x16}, - {0x37cd, 0x16}, - {0x37ce, 0x16}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x00}, - {0x3804, 0x0c}, - {0x3805, 0xdf}, - {0x3806, 0x09}, - {0x3807, 0xaf}, - {0x3808, 0x06}, - {0x3809, 0x68}, - {0x380a, 0x04}, - {0x380b, 0xd0}, - {0x380c, 0x0e}, - {0x380d, 0xec}, - {0x380e, 0x04}, - {0x380f, 0xe8}, - {0x3810, 0x00}, - {0x3811, 0x04}, - {0x3812, 0x00}, - {0x3813, 0x05}, - {0x3814, 0x03}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x10}, - {0x3820, 0x90}, - {0x3821, 0x67}, - {0x382a, 0x03}, - {0x382b, 0x01}, - {0x3830, 0x06}, - {0x3836, 0x02}, - {0x3862, 0x04}, - {0x3863, 0x08}, - {0x3cc0, 0x33}, - {0x3d85, 0x17}, - {0x3d8c, 0x73}, - {0x3d8d, 0xde}, - {0x4001, 0xe0}, - {0x4003, 0x40}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x400a, 0x00}, - {0x400b, 0x84}, - {0x400f, 0x80}, - {0x4010, 0xf0}, - {0x4011, 0xff}, - {0x4012, 0x02}, - {0x4013, 0x01}, - {0x4014, 0x01}, - {0x4015, 0x01}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4048, 0x00}, - {0x4049, 0x80}, - {0x4041, 0x03}, - {0x404c, 0x20}, - {0x404d, 0x00}, - {0x404e, 0x20}, - {0x4203, 0x80}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4503, 0x08}, - {0x4601, 0x80}, - {0x4800, 0x44}, - {0x4816, 0x53}, - {0x481b, 0x58}, - {0x481f, 0x27}, - {0x4837, 0x16}, - {0x483c, 0x0f}, - {0x484b, 0x05}, - {0x5000, 0x57}, - {0x5001, 0x0a}, - {0x5004, 0x06}, - {0x502e, 0x03}, - {0x5030, 0x41}, - {0x5780, 0x14}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x5795, 0x00}, - {0x5796, 0x10}, - {0x5797, 0x10}, - {0x5798, 0x73}, - {0x5799, 0x73}, - {0x579a, 0x00}, - {0x579b, 0x28}, - {0x579c, 0x00}, - {0x579d, 0x16}, - {0x579e, 0x06}, - {0x579f, 0x20}, - {0x57a0, 0x04}, - {0x57a1, 0xa0}, - {0x59f8, 0x3d}, - {0x5a08, 0x02}, - {0x5b00, 0x02}, - {0x5b01, 0x10}, - {0x5b02, 0x03}, - {0x5b03, 0xcf}, - {0x5b05, 0x6c}, - {0x5e00, 0x00} -}; - -static const struct ov8856_reg lane_4_mode_3264x2448[] = { - /* 3264x2448 resolution */ - {0x0103, 0x01}, - {0x0302, 0x3c}, - {0x0303, 0x01}, - {0x031e, 0x0c}, - {0x3000, 0x20}, - {0x3003, 0x08}, - {0x300e, 0x20}, - {0x3010, 0x00}, - {0x3015, 0x84}, - {0x3018, 0x72}, - {0x3021, 0x23}, - {0x3033, 0x24}, - {0x3500, 0x00}, - {0x3501, 0x9a}, - {0x3502, 0x20}, - {0x3503, 0x08}, - {0x3505, 0x83}, - {0x3508, 0x01}, - {0x3509, 0x80}, - {0x350c, 0x00}, - {0x350d, 0x80}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3600, 0x72}, - {0x3601, 0x40}, - {0x3602, 0x30}, - {0x3610, 0xc5}, - {0x3611, 0x58}, - {0x3612, 0x5c}, - {0x3613, 0xca}, - {0x3614, 0x60}, - {0x3628, 0xff}, - {0x3629, 0xff}, - {0x362a, 0xff}, - {0x3633, 0x10}, - {0x3634, 0x10}, - {0x3635, 0x10}, - {0x3636, 0x10}, - {0x3663, 0x08}, - {0x3669, 0x34}, - {0x366d, 0x00}, - {0x366e, 0x10}, - {0x3706, 0x86}, - {0x370b, 0x7e}, - {0x3714, 0x23}, - {0x3730, 0x12}, - {0x3733, 0x10}, - {0x3764, 0x00}, - {0x3765, 0x00}, - {0x3769, 0x62}, - {0x376a, 0x2a}, - {0x376b, 0x30}, - {0x3780, 0x00}, - {0x3781, 0x24}, - {0x3782, 0x00}, - {0x3783, 0x23}, - {0x3798, 0x2f}, - {0x37a1, 0x60}, - {0x37a8, 0x6a}, - {0x37ab, 0x3f}, - {0x37c2, 0x04}, - {0x37c3, 0xf1}, - {0x37c9, 0x80}, - {0x37cb, 0x16}, - {0x37cc, 0x16}, - {0x37cd, 0x16}, - {0x37ce, 0x16}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x0c}, - {0x3804, 0x0c}, - {0x3805, 0xdf}, - {0x3806, 0x09}, - {0x3807, 0xa3}, - {0x3808, 0x0c}, - {0x3809, 0xc0}, - {0x380a, 0x09}, - {0x380b, 0x90}, - {0x380c, 0x07}, - {0x380d, 0x8c}, - {0x380e, 0x09}, - {0x380f, 0xb2}, - {0x3810, 0x00}, - {0x3811, 0x04}, - {0x3812, 0x00}, - {0x3813, 0x02}, - {0x3814, 0x01}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x10}, - {0x3820, 0x80}, - {0x3821, 0x46}, - {0x382a, 0x01}, - {0x382b, 0x01}, - {0x3830, 0x06}, - {0x3836, 0x02}, - {0x3862, 0x04}, - {0x3863, 0x08}, - {0x3cc0, 0x33}, - {0x3d85, 0x17}, - {0x3d8c, 0x73}, - {0x3d8d, 0xde}, - {0x4001, 0xe0}, - {0x4003, 0x40}, - {0x4008, 0x00}, - {0x4009, 0x0b}, - {0x400a, 0x00}, - {0x400b, 0x84}, - {0x400f, 0x80}, - {0x4010, 0xf0}, - {0x4011, 0xff}, - {0x4012, 0x02}, - {0x4013, 0x01}, - {0x4014, 0x01}, - {0x4015, 0x01}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4048, 0x00}, - {0x4049, 0x80}, - {0x4041, 0x03}, - {0x404c, 0x20}, - {0x404d, 0x00}, - {0x404e, 0x20}, - {0x4203, 0x80}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4502, 0x50}, - {0x4503, 0x08}, - {0x4601, 0x80}, - {0x4800, 0x44}, - {0x4816, 0x53}, - {0x481b, 0x50}, - {0x481f, 0x27}, - {0x4823, 0x3c}, - {0x482b, 0x00}, - {0x4831, 0x66}, - {0x4837, 0x16}, - {0x483c, 0x0f}, - {0x484b, 0x05}, - {0x5000, 0x77}, - {0x5001, 0x0a}, - {0x5003, 0xc8}, - {0x5004, 0x04}, - {0x5006, 0x00}, - {0x5007, 0x00}, - {0x502e, 0x03}, - {0x5030, 0x41}, - {0x5780, 0x14}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x5795, 0x02}, - {0x5796, 0x20}, - {0x5797, 0x20}, - {0x5798, 0xd5}, - {0x5799, 0xd5}, - {0x579a, 0x00}, - {0x579b, 0x50}, - {0x579c, 0x00}, - {0x579d, 0x2c}, - {0x579e, 0x0c}, - {0x579f, 0x40}, - {0x57a0, 0x09}, - {0x57a1, 0x40}, - {0x59f8, 0x3d}, - {0x5a08, 0x02}, - {0x5b00, 0x02}, - {0x5b01, 0x10}, - {0x5b02, 0x03}, - {0x5b03, 0xcf}, - {0x5b05, 0x6c}, - {0x5e00, 0x00}, - {0x5e10, 0xfc} -}; - -static const struct ov8856_reg lane_4_mode_1632x1224[] = { - /* 1632x1224 resolution */ - {0x0103, 0x01}, - {0x0302, 0x3c}, - {0x0303, 0x01}, - {0x031e, 0x0c}, - {0x3000, 0x20}, - {0x3003, 0x08}, - {0x300e, 0x20}, - {0x3010, 0x00}, - {0x3015, 0x84}, - {0x3018, 0x72}, - {0x3021, 0x23}, - {0x3033, 0x24}, - {0x3500, 0x00}, - {0x3501, 0x4c}, - {0x3502, 0xe0}, - {0x3503, 0x08}, - {0x3505, 0x83}, - {0x3508, 0x01}, - {0x3509, 0x80}, - {0x350c, 0x00}, - {0x350d, 0x80}, - {0x350e, 0x04}, - {0x350f, 0x00}, - {0x3510, 0x00}, - {0x3511, 0x02}, - {0x3512, 0x00}, - {0x3600, 0x72}, - {0x3601, 0x40}, - {0x3602, 0x30}, - {0x3610, 0xc5}, - {0x3611, 0x58}, - {0x3612, 0x5c}, - {0x3613, 0xca}, - {0x3614, 0x60}, - {0x3628, 0xff}, - {0x3629, 0xff}, - {0x362a, 0xff}, - {0x3633, 0x10}, - {0x3634, 0x10}, - {0x3635, 0x10}, - {0x3636, 0x10}, - {0x3663, 0x08}, - {0x3669, 0x34}, - {0x366d, 0x00}, - {0x366e, 0x08}, - {0x3706, 0x86}, - {0x370b, 0x7e}, - {0x3714, 0x27}, - {0x3730, 0x12}, - {0x3733, 0x10}, - {0x3764, 0x00}, - {0x3765, 0x00}, - {0x3769, 0x62}, - {0x376a, 0x2a}, - {0x376b, 0x30}, - {0x3780, 0x00}, - {0x3781, 0x24}, - {0x3782, 0x00}, - {0x3783, 0x23}, - {0x3798, 0x2f}, - {0x37a1, 0x60}, - {0x37a8, 0x6a}, - {0x37ab, 0x3f}, - {0x37c2, 0x14}, - {0x37c3, 0xf1}, - {0x37c9, 0x80}, - {0x37cb, 0x16}, - {0x37cc, 0x16}, - {0x37cd, 0x16}, - {0x37ce, 0x16}, - {0x3800, 0x00}, - {0x3801, 0x00}, - {0x3802, 0x00}, - {0x3803, 0x0c}, - {0x3804, 0x0c}, - {0x3805, 0xdf}, - {0x3806, 0x09}, - {0x3807, 0xa3}, - {0x3808, 0x06}, - {0x3809, 0x60}, - {0x380a, 0x04}, - {0x380b, 0xc8}, - {0x380c, 0x07}, - {0x380d, 0x8c}, - {0x380e, 0x09}, - {0x380f, 0xb2}, - {0x3810, 0x00}, - {0x3811, 0x02}, - {0x3812, 0x00}, - {0x3813, 0x02}, - {0x3814, 0x03}, - {0x3815, 0x01}, - {0x3816, 0x00}, - {0x3817, 0x00}, - {0x3818, 0x00}, - {0x3819, 0x10}, - {0x3820, 0x80}, - {0x3821, 0x47}, - {0x382a, 0x03}, - {0x382b, 0x01}, - {0x3830, 0x06}, - {0x3836, 0x02}, - {0x3862, 0x04}, - {0x3863, 0x08}, - {0x3cc0, 0x33}, - {0x3d85, 0x17}, - {0x3d8c, 0x73}, - {0x3d8d, 0xde}, - {0x4001, 0xe0}, - {0x4003, 0x40}, - {0x4008, 0x00}, - {0x4009, 0x05}, - {0x400a, 0x00}, - {0x400b, 0x84}, - {0x400f, 0x80}, - {0x4010, 0xf0}, - {0x4011, 0xff}, - {0x4012, 0x02}, - {0x4013, 0x01}, - {0x4014, 0x01}, - {0x4015, 0x01}, - {0x4042, 0x00}, - {0x4043, 0x80}, - {0x4044, 0x00}, - {0x4045, 0x80}, - {0x4046, 0x00}, - {0x4047, 0x80}, - {0x4048, 0x00}, - {0x4049, 0x80}, - {0x4041, 0x03}, - {0x404c, 0x20}, - {0x404d, 0x00}, - {0x404e, 0x20}, - {0x4203, 0x80}, - {0x4307, 0x30}, - {0x4317, 0x00}, - {0x4502, 0x50}, - {0x4503, 0x08}, - {0x4601, 0x80}, - {0x4800, 0x44}, - {0x4816, 0x53}, - {0x481b, 0x50}, - {0x481f, 0x27}, - {0x4823, 0x3c}, - {0x482b, 0x00}, - {0x4831, 0x66}, - {0x4837, 0x16}, - {0x483c, 0x0f}, - {0x484b, 0x05}, - {0x5000, 0x77}, - {0x5001, 0x0a}, - {0x5003, 0xc8}, - {0x5004, 0x04}, - {0x5006, 0x00}, - {0x5007, 0x00}, - {0x502e, 0x03}, - {0x5030, 0x41}, - {0x5795, 0x00}, - {0x5796, 0x10}, - {0x5797, 0x10}, - {0x5798, 0x73}, - {0x5799, 0x73}, - {0x579a, 0x00}, - {0x579b, 0x28}, - {0x579c, 0x00}, - {0x579d, 0x16}, - {0x579e, 0x06}, - {0x579f, 0x20}, - {0x57a0, 0x04}, - {0x57a1, 0xa0}, - {0x5780, 0x14}, - {0x5781, 0x0f}, - {0x5782, 0x44}, - {0x5783, 0x02}, - {0x5784, 0x01}, - {0x5785, 0x01}, - {0x5786, 0x00}, - {0x5787, 0x04}, - {0x5788, 0x02}, - {0x5789, 0x0f}, - {0x578a, 0xfd}, - {0x578b, 0xf5}, - {0x578c, 0xf5}, - {0x578d, 0x03}, - {0x578e, 0x08}, - {0x578f, 0x0c}, - {0x5790, 0x08}, - {0x5791, 0x04}, - {0x5792, 0x00}, - {0x5793, 0x52}, - {0x5794, 0xa3}, - {0x59f8, 0x3d}, - {0x5a08, 0x02}, - {0x5b00, 0x02}, - {0x5b01, 0x10}, - {0x5b02, 0x03}, - {0x5b03, 0xcf}, - {0x5b05, 0x6c}, - {0x5e00, 0x00}, - {0x5e10, 0xfc} -}; - -static const struct ov8856_reg mipi_data_mbus_sbggr10_1x10[] = { +/* + * Xclk 24Mhz + */ +static const struct regval ov8856_global_regs_2lane[] = { + {0x0103, 0x01}, + {0x0302, 0x3c}, + {0x0303, 0x01}, + {0x031e, 0x0c}, + {0x3000, 0x00}, + {0x300e, 0x00}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x32}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x4c}, + {0x3502, 0xe0}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0x5a}, + {0x3614, 0x60}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366e, 0x08}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x27}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x14}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x03}, + {0x37cc, 0x0a}, + {0x37cd, 0x16}, + {0x37ce, 0x1f}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x06}, + {0x3809, 0x60}, + {0x380a, 0x04}, + {0x380b, 0xc8}, + {0x380c, 0x0e}, + {0x380d, 0xa0}, + {0x380e, 0x04}, + {0x380f, 0xde}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, {0x3813, 0x02}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x00}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4816, 0x53}, + {0x481b, 0x58}, + {0x481f, 0x27}, + {0x4837, 0x16}, + {0x5000, 0x77}, + {0x5030, 0x41}, + {0x5795, 0x00}, + {0x5796, 0x10}, + {0x5797, 0x10}, + {0x5798, 0x73}, + {0x5799, 0x73}, + {0x579a, 0x00}, + {0x579b, 0x28}, + {0x579c, 0x00}, + {0x579d, 0x16}, + {0x579e, 0x06}, + {0x579f, 0x20}, + {0x57a0, 0x04}, + {0x57a1, 0xa0}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00}, + {0x3820, 0x90}, + {0x3821, 0x67}, + {0x502e, 0x03}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x376b, 0x30}, + //{0x0100, 0x01}, + {REG_NULL, 0x00}, }; -static const struct ov8856_reg mipi_data_mbus_sgrbg10_1x10[] = { - {0x3813, 0x01}, +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + */ +static const struct regval ov8856_1632x1224_30fps_regs_2lane[] = { + {0x0100, 0x00}, + {0x0302, 0x3c}, + {0x0303, 0x01}, + {0x3501, 0x4c}, + {0x3502, 0xe0}, + {0x366e, 0x08}, + {0x3714, 0x27}, + {0x37c2, 0x14}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x06}, + {0x3809, 0x60}, + {0x380a, 0x04}, + {0x380b, 0xc8}, + {0x380c, 0x0e}, + {0x380d, 0xa0}, + {0x380e, 0x04}, + {0x380f, 0xde}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x03}, + {0x382a, 0x03}, + {0x4009, 0x05}, + {0x4837, 0x16}, + {0x4601, 0x80}, + {0x5795, 0x00}, + {0x5796, 0x10}, + {0x5797, 0x10}, + {0x5798, 0x73}, + {0x5799, 0x73}, + {0x579a, 0x00}, + {0x579b, 0x28}, + {0x579c, 0x00}, + {0x579d, 0x16}, + {0x579f, 0x20}, + {0x57a0, 0x04}, + {0x57a1, 0xa0}, + {0x3820, 0x90}, + {0x3821, 0x67}, + {0x502e, 0x03}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x376b, 0x30}, + //{0x0100, 0x01}, + {REG_NULL, 0x00}, }; -static const u32 ov8856_mbus_codes[] = { - MEDIA_BUS_FMT_SBGGR10_1X10, - MEDIA_BUS_FMT_SGRBG10_1X10 +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + */ +static const struct regval ov8856_3264x2448_30fps_regs_2lane[] = { + {0x0100, 0x00}, + {0x0302, 0x35}, + {0x0303, 0x00}, + {0x3501, 0x9a}, + {0x3502, 0x20}, + {0x366e, 0x10}, + {0x3714, 0x23}, + {0x37c2, 0x04}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x0c}, + {0x3809, 0xc0}, + {0x380a, 0x09}, + {0x380b, 0x90}, + {0x380c, 0x07}, + {0x380d, 0x8c}, + {0x380e, 0x09}, + {0x380f, 0xb2}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x01}, + {0x382a, 0x01}, + {0x4009, 0x0b}, + {0x4837, 0x0c}, + {0x4601, 0x80}, + {0x5795, 0x02}, + {0x5796, 0x20}, + {0x5797, 0x20}, + {0x5798, 0xd5}, + {0x5799, 0xd5}, + {0x579a, 0x00}, + {0x579b, 0x50}, + {0x579c, 0x00}, + {0x579d, 0x2c}, + {0x579f, 0x40}, + {0x57a0, 0x09}, + {0x57a1, 0x40}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x502e, 0x03}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x376b, 0x30}, + {0x0100, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + */ +static const struct regval ov8856_global_regs_4lane[] = { + {0x0103, 0x01}, + {0x0302, 0x3c}, + {0x0303, 0x01}, + {0x031e, 0x0c}, + {0x3000, 0x00}, + {0x300e, 0x00}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x4c}, + {0x3502, 0xe0}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0x5a}, + {0x3614, 0x60}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366e, 0x08}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x27}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x14}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x03}, + {0x37cc, 0x0a}, + {0x37cd, 0x16}, + {0x37ce, 0x1f}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x06}, + {0x3809, 0x60}, + {0x380a, 0x04}, + {0x380b, 0xc8}, + {0x380c, 0x07}, + {0x380d, 0x8c}, + {0x380e, 0x09}, + {0x380f, 0xb2}, + {0x3810, 0x00}, + {0x3811, 0x08}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x00}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4816, 0x53}, + {0x481b, 0x58}, + {0x481f, 0x27}, + {0x4837, 0x16}, + {0x5000, 0x77}, + {0x5030, 0x41}, + {0x5795, 0x00}, + {0x5796, 0x10}, + {0x5797, 0x10}, + {0x5798, 0x73}, + {0x5799, 0x73}, + {0x579a, 0x00}, + {0x579b, 0x28}, + {0x579c, 0x00}, + {0x579d, 0x16}, + {0x579e, 0x06}, + {0x579f, 0x20}, + {0x57a0, 0x04}, + {0x57a1, 0xa0}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00}, + {0x3820, 0x90}, + {0x3821, 0x67}, + {0x502e, 0x03}, + {0x5001, 0x0a}, + {0x5004, 0x04}, + {0x376b, 0x30}, + //{0x0100, 0x01}, + {REG_NULL, 0x00}, +}; + +/* + * Xclk 24Mhz + * max_framerate 30fps + * mipi_datarate per lane 720Mbps + */ +static const struct regval ov8856_3264x2448_30fps_regs_4lane[] = { + {0x0100, 0x00}, + {0x3501, 0x9a}, + {0x3502, 0x20}, + {0x366e, 0x10}, + {0x3714, 0x23}, + {0x37c2, 0x04}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x0c}, + {0x3809, 0xc0}, + {0x380a, 0x09}, + {0x380b, 0x90}, + {0x380c, 0x07}, + {0x380d, 0x8c}, + {0x380e, 0x09}, + {0x380f, 0xb2}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x01}, + {0x382a, 0x01}, + {0x4009, 0x0b}, + {0x4601, 0x80}, + {0x5795, 0x02}, + {0x5796, 0x20}, + {0x5797, 0x20}, + {0x5798, 0xd5}, + {0x5799, 0xd5}, + {0x579a, 0x00}, + {0x579b, 0x50}, + {0x579c, 0x00}, + {0x579d, 0x2c}, + {0x579e, 0x0c}, + {0x579f, 0x40}, + {0x57a0, 0x09}, + {0x57a1, 0x40}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x502e, 0x03}, + {0x5001, 0x02}, + {0x5004, 0x04}, + {0x376b, 0x30}, + //{0x0100, 0x01}, + {REG_NULL, 0x00}, +}; + + +static const struct ov8856_mode supported_modes_2lane[] = { + { + .width = 3264, + .height = 2448, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x09ac, + .hts_def = 0x078c * 2, + .vts_def = 0x09b2, + .reg_list = ov8856_3264x2448_30fps_regs_2lane, + }, + + { + .width = 1632, + .height = 1224, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x04d0, + .hts_def = 0x0ea0, + .vts_def = 0x04de, + .reg_list = ov8856_1632x1224_30fps_regs_2lane, + }, + +}; + +static const struct ov8856_mode supported_modes_4lane[] = { + { + .width = 3264, + .height = 2448, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x09ac, + .hts_def = 0x078c * 2, + .vts_def = 0x0A00, + .reg_list = ov8856_3264x2448_30fps_regs_4lane, + }, +}; + +static const struct ov8856_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ }; static const char * const ov8856_test_pattern_menu[] = { @@ -1402,480 +794,907 @@ static const char * const ov8856_test_pattern_menu[] = { "Bottom-Top Darker Color Bar" }; -static const struct ov8856_reg_list bayer_offset_configs[] = { - [OV8856_MEDIA_BUS_FMT_SBGGR10_1X10] = { - .num_of_regs = ARRAY_SIZE(mipi_data_mbus_sbggr10_1x10), - .regs = mipi_data_mbus_sbggr10_1x10, - }, - [OV8856_MEDIA_BUS_FMT_SGRBG10_1X10] = { - .num_of_regs = ARRAY_SIZE(mipi_data_mbus_sgrbg10_1x10), - .regs = mipi_data_mbus_sgrbg10_1x10, - } -}; - -struct ov8856 { - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_ctrl_handler ctrl_handler; - - struct clk *xvclk; - struct gpio_desc *reset_gpio; - struct regulator_bulk_data supplies[ARRAY_SIZE(ov8856_supply_names)]; - - /* V4L2 Controls */ - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *exposure; - - /* Current mode */ - const struct ov8856_mode *cur_mode; - - /* Application specified mbus format */ - u32 cur_mbus_index; - - /* To serialize asynchronus callbacks */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; - - /* lanes index */ - u8 nlanes; - - const struct ov8856_lane_cfg *priv_lane; - u8 modes_size; - - /* True if the device has been identified */ - bool identified; -}; - -struct ov8856_lane_cfg { - const s64 link_freq_menu_items[2]; - const struct ov8856_link_freq_config link_freq_configs[2]; - const struct ov8856_mode supported_modes[4]; -}; - -static const struct ov8856_lane_cfg lane_cfg_2 = { - { - 720000000, - 360000000, - }, - {{ - .reg_list = { - .num_of_regs = - ARRAY_SIZE(mipi_data_rate_lane_2.regs_0), - .regs = mipi_data_rate_lane_2.regs_0, - } - }, - { - .reg_list = { - .num_of_regs = - ARRAY_SIZE(mipi_data_rate_lane_2.regs_1), - .regs = mipi_data_rate_lane_2.regs_1, - } - }}, - {{ - .width = 3280, - .height = 2464, - .hts = 1928, - .vts_def = 2488, - .vts_min = 2488, - .reg_list = { - .num_of_regs = - ARRAY_SIZE(lane_2_mode_3280x2464), - .regs = lane_2_mode_3280x2464, - }, - .link_freq_index = 0, - .data_lanes = 2, - .default_mbus_index = OV8856_MEDIA_BUS_FMT_SGRBG10_1X10, - }, - { - .width = 1640, - .height = 1232, - .hts = 3168, - .vts_def = 1514, - .vts_min = 1514, - .reg_list = { - .num_of_regs = - ARRAY_SIZE(lane_2_mode_1640x1232), - .regs = lane_2_mode_1640x1232, - }, - .link_freq_index = 1, - .data_lanes = 2, - .default_mbus_index = OV8856_MEDIA_BUS_FMT_SGRBG10_1X10, - }} -}; - -static const struct ov8856_lane_cfg lane_cfg_4 = { - { - 360000000, - 180000000, - }, - {{ - .reg_list = { - .num_of_regs = - ARRAY_SIZE(mipi_data_rate_lane_4.regs_0), - .regs = mipi_data_rate_lane_4.regs_0, - } - }, - { - .reg_list = { - .num_of_regs = - ARRAY_SIZE(mipi_data_rate_lane_4.regs_1), - .regs = mipi_data_rate_lane_4.regs_1, - } - }}, - {{ - .width = 3280, - .height = 2464, - .hts = 1928, - .vts_def = 2488, - .vts_min = 2488, - .reg_list = { - .num_of_regs = - ARRAY_SIZE(lane_4_mode_3280x2464), - .regs = lane_4_mode_3280x2464, - }, - .link_freq_index = 0, - .data_lanes = 4, - .default_mbus_index = OV8856_MEDIA_BUS_FMT_SGRBG10_1X10, - }, - { - .width = 1640, - .height = 1232, - .hts = 3820, - .vts_def = 1256, - .vts_min = 1256, - .reg_list = { - .num_of_regs = - ARRAY_SIZE(lane_4_mode_1640x1232), - .regs = lane_4_mode_1640x1232, - }, - .link_freq_index = 1, - .data_lanes = 4, - .default_mbus_index = OV8856_MEDIA_BUS_FMT_SGRBG10_1X10, - }, - { - .width = 3264, - .height = 2448, - .hts = 1932, - .vts_def = 2482, - .vts_min = 2482, - .reg_list = { - .num_of_regs = - ARRAY_SIZE(lane_4_mode_3264x2448), - .regs = lane_4_mode_3264x2448, - }, - .link_freq_index = 0, - .data_lanes = 4, - .default_mbus_index = OV8856_MEDIA_BUS_FMT_SBGGR10_1X10, - }, - { - .width = 1632, - .height = 1224, - .hts = 1932, - .vts_def = 2482, - .vts_min = 2482, - .reg_list = { - .num_of_regs = - ARRAY_SIZE(lane_4_mode_1632x1224), - .regs = lane_4_mode_1632x1224, - }, - .link_freq_index = 1, - .data_lanes = 4, - .default_mbus_index = OV8856_MEDIA_BUS_FMT_SBGGR10_1X10, - }} -}; - -static unsigned int ov8856_modes_num(const struct ov8856 *ov8856) +/* Write registers up to 4 at a time */ +static int ov8856_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) { - unsigned int i, count = 0; + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; - for (i = 0; i < ARRAY_SIZE(ov8856->priv_lane->supported_modes); i++) { - if (ov8856->priv_lane->supported_modes[i].width == 0) - break; - count++; - } - - return count; -} - -static u64 to_rate(const s64 *link_freq_menu_items, - u32 f_index, u8 nlanes) -{ - u64 pixel_rate = link_freq_menu_items[f_index] * 2 * nlanes; - - do_div(pixel_rate, OV8856_RGB_DEPTH); - - return pixel_rate; -} - -static u64 to_pixels_per_line(const s64 *link_freq_menu_items, u32 hts, - u32 f_index, u8 nlanes) -{ - u64 ppl = hts * to_rate(link_freq_menu_items, f_index, nlanes); - - do_div(ppl, OV8856_SCLK); - - return ppl; -} - -static int ov8856_read_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = {0}; - int ret; + dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); if (len > 4) return -EINVAL; - put_unaligned_be16(reg, addr_buf); + buf[0] = reg >> 8; + buf[1] = reg & 0xff; + + val_be = cpu_to_be32(val); + val_p = (u8 *)&val_be; + buf_i = 2; + val_i = 4 - len; + + while (val_i < 4) + buf[buf_i++] = val_p[val_i++]; + + if (i2c_master_send(client, buf, len + 2) != len + 2) { + dev_err(&client->dev, "write reg(0x%x val:0x%x)! failed !\n", reg, val); + return -EIO; + } + + return 0; +} + +static int ov8856_write_array(struct i2c_client *client, + const struct regval *regs) +{ + u32 i; + int ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) + ret = ov8856_write_reg(client, regs[i].addr, + OV8856_REG_VALUE_08BIT, + regs[i].val); + + return ret; +} + +/* Read registers up to 4 at a time */ +static int ov8856_read_reg(struct i2c_client *client, u16 reg, + unsigned int len, u32 *val) +{ + struct i2c_msg msgs[2]; + u8 *data_be_p; + __be32 data_be = 0; + __be16 reg_addr_be = cpu_to_be16(reg); + int ret; + + if (len > 4 || !len) + return -EINVAL; + + data_be_p = (u8 *)&data_be; + /* Write register address */ msgs[0].addr = client->addr; msgs[0].flags = 0; - msgs[0].len = sizeof(addr_buf); - msgs[0].buf = addr_buf; + msgs[0].len = 2; + msgs[0].buf = (u8 *)®_addr_be; + + /* Read data from register */ msgs[1].addr = client->addr; msgs[1].flags = I2C_M_RD; msgs[1].len = len; - msgs[1].buf = &data_buf[4 - len]; + msgs[1].buf = &data_be_p[4 - len]; ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - if (ret != ARRAY_SIZE(msgs)) + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read reg:0x%x failed !\n", reg); return -EIO; + } - *val = get_unaligned_be32(data_buf); + *val = be32_to_cpu(data_be); + dev_dbg(&client->dev, "read reg:0x%x value(0x%x) !\n", reg, *val); return 0; } -static int ov8856_write_reg(struct ov8856 *ov8856, u16 reg, u16 len, u32 val) +static int ov8856_get_reso_dist(const struct ov8856_mode *mode, + struct v4l2_mbus_framefmt *framefmt) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - u8 buf[6]; - - if (len > 4) - return -EINVAL; - - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << 8 * (4 - len), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); } -static int ov8856_write_reg_list(struct ov8856 *ov8856, - const struct ov8856_reg_list *r_list) +static const struct ov8856_mode * +ov8856_find_best_fit(struct ov8856 *ov8856, + struct v4l2_subdev_format *fmt) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; unsigned int i; - int ret; - for (i = 0; i < r_list->num_of_regs; i++) { - ret = ov8856_write_reg(ov8856, r_list->regs[i].address, 1, - r_list->regs[i].val); - if (ret) { - dev_err_ratelimited(&client->dev, - "failed to write reg 0x%4.4x. error = %d", - r_list->regs[i].address, ret); - return ret; + for (i = 0; i < ov8856->cfg_num; i++) { + dist = ov8856_get_reso_dist(&supported_modes[i], framefmt); + if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) { + cur_best_fit_dist = dist; + cur_best_fit = i; } } - return 0; + return &supported_modes[cur_best_fit]; } -static int ov8856_identify_module(struct ov8856 *ov8856) +static int ov8856_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - int ret; - u32 val; + struct ov8856 *ov8856 = to_ov8856(sd); + const struct ov8856_mode *mode; + s64 h_blank, vblank_def; - if (ov8856->identified) - return 0; + mutex_lock(&ov8856->mutex); - ret = ov8856_read_reg(ov8856, OV8856_REG_CHIP_ID, - OV8856_REG_VALUE_24BIT, &val); - if (ret) - return ret; - - if (val != OV8856_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", - OV8856_CHIP_ID, val); - return -ENXIO; + mode = ov8856_find_best_fit(ov8856, fmt); + fmt->format.code = OV8856_MEDIA_BUS_FMT; + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.field = V4L2_FIELD_NONE; + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; +#else + mutex_unlock(&ov8856->mutex); + return -ENOTTY; +#endif + } else { + ov8856->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(ov8856->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(ov8856->vblank, vblank_def, + OV8856_VTS_MAX - mode->height, + 1, vblank_def); } - ov8856->identified = true; + mutex_unlock(&ov8856->mutex); return 0; } -static int ov8856_update_digital_gain(struct ov8856 *ov8856, u32 d_gain) +static int ov8856_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_format *fmt) { - return ov8856_write_reg(ov8856, OV8856_REG_DIGITAL_GAIN, - OV8856_REG_VALUE_16BIT, d_gain); + struct ov8856 *ov8856 = to_ov8856(sd); + const struct ov8856_mode *mode = ov8856->cur_mode; + + mutex_lock(&ov8856->mutex); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + fmt->format = *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); +#else + mutex_unlock(&ov8856->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = OV8856_MEDIA_BUS_FMT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&ov8856->mutex); + + return 0; } -static int ov8856_test_pattern(struct ov8856 *ov8856, u32 pattern) +static int ov8856_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_mbus_code_enum *code) { + if (code->index != 0) + return -EINVAL; + code->code = OV8856_MEDIA_BUS_FMT; + + return 0; +} + +static int ov8856_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + + if (fse->index >= ov8856->cfg_num) + return -EINVAL; + + if (fse->code != OV8856_MEDIA_BUS_FMT) + return -EINVAL; + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = supported_modes[fse->index].width; + fse->max_height = supported_modes[fse->index].height; + fse->min_height = supported_modes[fse->index].height; + + return 0; +} + +static int ov8856_enable_test_pattern(struct ov8856 *ov8856, u32 pattern) +{ + u32 val; + if (pattern) - pattern = (pattern - 1) << OV8856_TEST_PATTERN_BAR_SHIFT | - OV8856_TEST_PATTERN_ENABLE; + val = (pattern - 1) | OV8856_TEST_PATTERN_ENABLE; + else + val = OV8856_TEST_PATTERN_DISABLE; - return ov8856_write_reg(ov8856, OV8856_REG_TEST_PATTERN, - OV8856_REG_VALUE_08BIT, pattern); + return ov8856_write_reg(ov8856->client, + OV8856_REG_TEST_PATTERN, + OV8856_REG_VALUE_08BIT, + val); } -static int ov8856_set_ctrl_hflip(struct ov8856 *ov8856, u32 ctrl_val) +static int ov8856_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) { - int ret; - u32 val; + struct ov8856 *ov8856 = to_ov8856(sd); + const struct ov8856_mode *mode = ov8856->cur_mode; +// printk("%s yyk\n", __func__); + mutex_lock(&ov8856->mutex); + fi->interval = mode->max_fps; + mutex_unlock(&ov8856->mutex); - ret = ov8856_read_reg(ov8856, OV8856_REG_MIRROR_OPT_1, - OV8856_REG_VALUE_08BIT, &val); - if (ret) - return ret; + return 0; +} - ret = ov8856_write_reg(ov8856, OV8856_REG_MIRROR_OPT_1, - OV8856_REG_VALUE_08BIT, - ctrl_val ? val & ~OV8856_REG_MIRROR_OP_2 : - val | OV8856_REG_MIRROR_OP_2); +static void ov8856_get_otp(struct otp_info *otp, + struct rkmodule_inf *inf) +{ + u32 i, j; + u32 w, h; - if (ret) - return ret; + /* awb */ + if (otp->awb_data.flag) { + inf->awb.flag = 1; + inf->awb.r_value = otp->awb_data.r_ratio; + inf->awb.b_value = otp->awb_data.b_ratio; + inf->awb.gr_value = otp->awb_data.g_ratio; + inf->awb.gb_value = 0x0; - ret = ov8856_read_reg(ov8856, OV8856_REG_FORMAT2, - OV8856_REG_VALUE_08BIT, &val); - if (ret) - return ret; + inf->awb.golden_r_value = otp->awb_data.r_golden; + inf->awb.golden_b_value = otp->awb_data.b_golden; + inf->awb.golden_gr_value = otp->awb_data.g_golden; + inf->awb.golden_gb_value = 0x0; + } - return ov8856_write_reg(ov8856, OV8856_REG_FORMAT2, + /* lsc */ + if (otp->lsc_data.flag) { + inf->lsc.flag = 1; + inf->lsc.width = otp->basic_data.size.width; + inf->lsc.height = otp->basic_data.size.height; + inf->lsc.table_size = otp->lsc_data.table_size; + + for (i = 0; i < 289; i++) { + inf->lsc.lsc_r[i] = (otp->lsc_data.data[i * 2] << 8) | + otp->lsc_data.data[i * 2 + 1]; + inf->lsc.lsc_gr[i] = (otp->lsc_data.data[i * 2 + 578] << 8) | + otp->lsc_data.data[i * 2 + 579]; + inf->lsc.lsc_gb[i] = (otp->lsc_data.data[i * 2 + 1156] << 8) | + otp->lsc_data.data[i * 2 + 1157]; + inf->lsc.lsc_b[i] = (otp->lsc_data.data[i * 2 + 1734] << 8) | + otp->lsc_data.data[i * 2 + 1735]; + } + } + + /* pdaf */ + if (otp->pdaf_data.flag) { + inf->pdaf.flag = 1; + inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width; + inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height; + inf->pdaf.pd_offset = otp->pdaf_data.pd_offset; + inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode; + inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir; + inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width; + inf->pdaf.dccmap_height = otp->pdaf_data.dccmap_height; + w = otp->pdaf_data.gainmap_width; + h = otp->pdaf_data.gainmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.gainmap[i * w + j] = + (otp->pdaf_data.gainmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.gainmap[(i * w + j) * 2 + 1]; + } + } + w = otp->pdaf_data.dccmap_width; + h = otp->pdaf_data.dccmap_height; + for (i = 0; i < h; i++) { + for (j = 0; j < w; j++) { + inf->pdaf.dccmap[i * w + j] = + (otp->pdaf_data.dccmap[(i * w + j) * 2] << 8) | + otp->pdaf_data.dccmap[(i * w + j) * 2 + 1]; + } + } + } + + /* af */ + if (otp->af_data.flag) { + inf->af.flag = 1; + inf->af.dir_cnt = 1; + inf->af.af_otp[0].vcm_start = otp->af_data.af_inf; + inf->af.af_otp[0].vcm_end = otp->af_data.af_macro; + inf->af.af_otp[0].vcm_dir = 0; + } + +} + +static void ov8856_get_module_inf(struct ov8856 *ov8856, + struct rkmodule_inf *inf) +{ + struct otp_info *otp = ov8856->otp; + + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, OV8856_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, ov8856->module_name, sizeof(inf->base.module)); + strscpy(inf->base.lens, ov8856->len_name, sizeof(inf->base.lens)); + if (otp) + ov8856_get_otp(otp, inf); + +} + +static void ov8856_set_awb_cfg(struct ov8856 *ov8856, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&ov8856->mutex); + memcpy(&ov8856->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&ov8856->mutex); +} + +static void ov8856_set_lsc_cfg(struct ov8856 *ov8856, + struct rkmodule_lsc_cfg *cfg) +{ + mutex_lock(&ov8856->mutex); + memcpy(&ov8856->lsc_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&ov8856->mutex); +} + +static long ov8856_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + long ret = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + ov8856_get_module_inf(ov8856, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + ov8856_set_awb_cfg(ov8856, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_LSC_CFG: + ov8856_set_lsc_cfg(ov8856, (struct rkmodule_lsc_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = ov8856_write_reg(ov8856->client, + OV8856_REG_CTRL_MODE, OV8856_REG_VALUE_08BIT, - ctrl_val ? val & ~OV8856_REG_FORMAT2_OP_1 & - ~OV8856_REG_FORMAT2_OP_2 & - ~OV8856_REG_FORMAT2_OP_3 : - val | OV8856_REG_FORMAT2_OP_1 | - OV8856_REG_FORMAT2_OP_2 | - OV8856_REG_FORMAT2_OP_3); + OV8856_MODE_STREAMING); + else + ret = ov8856_write_reg(ov8856->client, + OV8856_REG_CTRL_MODE, + OV8856_REG_VALUE_08BIT, + OV8856_MODE_SW_STANDBY); + break; + default: + ret = -ENOTTY; + break; + } + + return ret; } -static int ov8856_set_ctrl_vflip(struct ov8856 *ov8856, u8 ctrl_val) +#ifdef CONFIG_COMPAT +static long ov8856_compat_ioctl32(struct v4l2_subdev *sd, + unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rkmodule_inf *inf; + struct rkmodule_awb_cfg *awb_cfg; + struct rkmodule_lsc_cfg *lsc_cfg; + long ret = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (!inf) { + ret = -ENOMEM; + return ret; + } + + ret = ov8856_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(awb_cfg, up, sizeof(*awb_cfg)); + if (!ret) + ret = ov8856_ioctl(sd, cmd, awb_cfg); + else + ret = -EFAULT; + kfree(awb_cfg); + break; + case RKMODULE_LSC_CFG: + lsc_cfg = kzalloc(sizeof(*lsc_cfg), GFP_KERNEL); + if (!lsc_cfg) { + ret = -ENOMEM; + return ret; + } + + ret = copy_from_user(lsc_cfg, up, sizeof(*lsc_cfg)); + if (!ret) + ret = ov8856_ioctl(sd, cmd, lsc_cfg); + else + ret = -EFAULT; + kfree(lsc_cfg); + break; + case RKMODULE_SET_QUICK_STREAM: + ret = copy_from_user(&stream, up, sizeof(u32)); + if (!ret) + ret = ov8856_ioctl(sd, cmd, &stream); + else + ret = -EFAULT; + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} +#endif + +static int __ov8856_start_stream(struct ov8856 *ov8856) { int ret; - u32 val; - ret = ov8856_read_reg(ov8856, OV8856_REG_FLIP_OPT_1, - OV8856_REG_VALUE_08BIT, &val); + ret = ov8856_write_array(ov8856->client, ov8856->cur_mode->reg_list); if (ret) return ret; - ret = ov8856_write_reg(ov8856, OV8856_REG_FLIP_OPT_1, - OV8856_REG_VALUE_08BIT, - ctrl_val ? val | OV8856_REG_FLIP_OP_1 | - OV8856_REG_FLIP_OP_2 : - val & ~OV8856_REG_FLIP_OP_1 & - ~OV8856_REG_FLIP_OP_2); - - ret = ov8856_read_reg(ov8856, OV8856_REG_FLIP_OPT_2, - OV8856_REG_VALUE_08BIT, &val); + /* In case these controls are set before streaming */ + mutex_unlock(&ov8856->mutex); + ret = v4l2_ctrl_handler_setup(&ov8856->ctrl_handler); + mutex_lock(&ov8856->mutex); if (ret) return ret; - ret = ov8856_write_reg(ov8856, OV8856_REG_FLIP_OPT_2, - OV8856_REG_VALUE_08BIT, - ctrl_val ? val | OV8856_REG_FLIP_OP_2 : - val & ~OV8856_REG_FLIP_OP_2); - - ret = ov8856_read_reg(ov8856, OV8856_REG_FLIP_OPT_3, - OV8856_REG_VALUE_08BIT, &val); - if (ret) - return ret; - - ret = ov8856_write_reg(ov8856, OV8856_REG_FLIP_OPT_3, - OV8856_REG_VALUE_08BIT, - ctrl_val ? val & ~OV8856_REG_FLIP_OP_0 & - ~OV8856_REG_FLIP_OP_1 : - val | OV8856_REG_FLIP_OP_0 | - OV8856_REG_FLIP_OP_1); - - ret = ov8856_read_reg(ov8856, OV8856_REG_FORMAT1, - OV8856_REG_VALUE_08BIT, &val); - if (ret) - return ret; - - return ov8856_write_reg(ov8856, OV8856_REG_FORMAT1, - OV8856_REG_VALUE_08BIT, - ctrl_val ? val | OV8856_REG_FORMAT1_OP_1 | - OV8856_REG_FORMAT1_OP_3 | - OV8856_REG_FORMAT1_OP_2 : - val & ~OV8856_REG_FORMAT1_OP_1 & - ~OV8856_REG_FORMAT1_OP_3 & - ~OV8856_REG_FORMAT1_OP_2); + return ov8856_write_reg(ov8856->client, + OV8856_REG_CTRL_MODE, + OV8856_REG_VALUE_08BIT, + OV8856_MODE_STREAMING); } +static int __ov8856_stop_stream(struct ov8856 *ov8856) +{ + return ov8856_write_reg(ov8856->client, + OV8856_REG_CTRL_MODE, + OV8856_REG_VALUE_08BIT, + OV8856_MODE_SW_STANDBY); +} + +static int ov8856_s_stream(struct v4l2_subdev *sd, int on) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + struct i2c_client *client = ov8856->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + ov8856->cur_mode->width, + ov8856->cur_mode->height, + DIV_ROUND_CLOSEST(ov8856->cur_mode->max_fps.denominator, + ov8856->cur_mode->max_fps.numerator)); + + mutex_lock(&ov8856->mutex); + on = !!on; + if (on == ov8856->streaming) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __ov8856_start_stream(ov8856); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + + } else { + __ov8856_stop_stream(ov8856); + pm_runtime_put(&client->dev); + } + + ov8856->streaming = on; + +unlock_and_return: + mutex_unlock(&ov8856->mutex); + + return ret; +} + +static int ov8856_s_power(struct v4l2_subdev *sd, int on) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + struct i2c_client *client = ov8856->client; + int ret = 0; + + dev_info(&client->dev, "%s on(%d)\n", __func__, on); + + mutex_lock(&ov8856->mutex); + + /* If the power state is not modified - no work to do. */ + if (ov8856->power_on == !!on) + goto unlock_and_return; + + if (on) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = ov8856_write_array(ov8856->client, ov8856_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ov8856->power_on = true; + } else { + pm_runtime_put(&client->dev); + ov8856->power_on = false; + } + +unlock_and_return: + mutex_unlock(&ov8856->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 ov8856_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, OV8856_XVCLK_FREQ / 1000 / 1000); +} + +static int __ov8856_power_on(struct ov8856 *ov8856) +{ + int ret; + u32 delay_us; + struct device *dev = &ov8856->client->dev; + + if (!IS_ERR(ov8856->power_gpio)) + gpiod_set_value_cansleep(ov8856->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(ov8856->pins_default)) { + ret = pinctrl_select_state(ov8856->pinctrl, + ov8856->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + + ret = clk_set_rate(ov8856->xvclk, OV8856_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(ov8856->xvclk) != OV8856_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + ret = clk_prepare_enable(ov8856->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + if (!IS_ERR(ov8856->reset_gpio)) + gpiod_set_value_cansleep(ov8856->reset_gpio, 0); + + ret = regulator_bulk_enable(OV8856_NUM_SUPPLIES, ov8856->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(ov8856->reset_gpio)) + gpiod_set_value_cansleep(ov8856->reset_gpio, 1); + + usleep_range(1000, 2000); + if (!IS_ERR(ov8856->pwdn_gpio)) + gpiod_set_value_cansleep(ov8856->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = ov8856_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + + return 0; + +disable_clk: + clk_disable_unprepare(ov8856->xvclk); + + return ret; +} + +static void __ov8856_power_off(struct ov8856 *ov8856) +{ + int ret; + struct device *dev = &ov8856->client->dev; + + if (!IS_ERR(ov8856->pwdn_gpio)) + gpiod_set_value_cansleep(ov8856->pwdn_gpio, 0); + clk_disable_unprepare(ov8856->xvclk); + if (!IS_ERR(ov8856->reset_gpio)) + gpiod_set_value_cansleep(ov8856->reset_gpio, 0); + if (!IS_ERR_OR_NULL(ov8856->pins_sleep)) { + ret = pinctrl_select_state(ov8856->pinctrl, + ov8856->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + + if (!IS_ERR(ov8856->power_gpio)) + gpiod_set_value_cansleep(ov8856->power_gpio, 0); + regulator_bulk_disable(OV8856_NUM_SUPPLIES, ov8856->supplies); + +} + +static int __maybe_unused ov8856_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + + return __ov8856_power_on(ov8856); +} + +static int __maybe_unused ov8856_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov8856 *ov8856 = to_ov8856(sd); + + __ov8856_power_off(ov8856); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->state, 0); + const struct ov8856_mode *def_mode = &supported_modes[0]; + + mutex_lock(&ov8856->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = OV8856_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&ov8856->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int ov8856_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + + if (fie->index >= ov8856->cfg_num) + return -EINVAL; + + if (fie->code != OV8856_MEDIA_BUS_FMT) + return -EINVAL; + + fie->width = supported_modes[fie->index].width; + fie->height = supported_modes[fie->index].height; + fie->interval = supported_modes[fie->index].max_fps; + return 0; +} + +static int ov8856_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + struct ov8856 *sensor = to_ov8856(sd); + struct device *dev = &sensor->client->dev; + + dev_info(dev, "%s(%d) enter!\n", __func__, __LINE__); + + if (2 == sensor->lane_num || 4 == sensor->lane_num) { + config->type = V4L2_MBUS_CSI2_DPHY; + config->bus.mipi_csi2.num_data_lanes = sensor->lane_num; + } else { + dev_err(&sensor->client->dev, + "unsupported lane_num(%d)\n", sensor->lane_num); + } + return 0; +} + +static int ov8856_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct ov8856 *ov8856 = to_ov8856(sd); + + if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) { + sel->r.left = 0; + sel->r.width = ov8856->cur_mode->width; + sel->r.top = 0; + sel->r.height = ov8856->cur_mode->height; + return 0; + } + + return -EINVAL; +} + +static const struct dev_pm_ops ov8856_pm_ops = { + SET_RUNTIME_PM_OPS(ov8856_runtime_suspend, + ov8856_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops ov8856_internal_ops = { + .open = ov8856_open, +}; +#endif + +static const struct v4l2_subdev_core_ops ov8856_core_ops = { + .s_power = ov8856_s_power, + .ioctl = ov8856_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ov8856_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops ov8856_video_ops = { + .s_stream = ov8856_s_stream, + .g_frame_interval = ov8856_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops ov8856_pad_ops = { + .enum_mbus_code = ov8856_enum_mbus_code, + .enum_frame_size = ov8856_enum_frame_sizes, + .enum_frame_interval = ov8856_enum_frame_interval, + .get_fmt = ov8856_get_fmt, + .set_fmt = ov8856_set_fmt, + .get_selection = ov8856_get_selection, + .get_mbus_config = ov8856_g_mbus_config, +}; + +static const struct v4l2_subdev_ops ov8856_subdev_ops = { + .core = &ov8856_core_ops, + .video = &ov8856_video_ops, + .pad = &ov8856_pad_ops, +}; static int ov8856_set_ctrl(struct v4l2_ctrl *ctrl) { struct ov8856 *ov8856 = container_of(ctrl->handler, struct ov8856, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - s64 exposure_max; + struct i2c_client *client = ov8856->client; + s64 max; int ret = 0; + u32 val = 0; /* Propagate change of current control to all related controls */ - if (ctrl->id == V4L2_CID_VBLANK) { + switch (ctrl->id) { + case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - exposure_max = ov8856->cur_mode->height + ctrl->val - - OV8856_EXPOSURE_MAX_MARGIN; + max = ov8856->cur_mode->height + ctrl->val - 4; __v4l2_ctrl_modify_range(ov8856->exposure, - ov8856->exposure->minimum, - exposure_max, ov8856->exposure->step, - exposure_max); + ov8856->exposure->minimum, max, + ov8856->exposure->step, + ov8856->exposure->default_value); + break; } - /* V4L2 controls values will be applied only when power is already up */ if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - ret = ov8856_write_reg(ov8856, OV8856_REG_ANALOG_GAIN, - OV8856_REG_VALUE_16BIT, ctrl->val); - break; - - case V4L2_CID_DIGITAL_GAIN: - ret = ov8856_update_digital_gain(ov8856, ctrl->val); - break; - case V4L2_CID_EXPOSURE: /* 4 least significant bits of expsoure are fractional part */ - ret = ov8856_write_reg(ov8856, OV8856_REG_EXPOSURE, - OV8856_REG_VALUE_24BIT, ctrl->val << 4); + dev_dbg(&client->dev, "set exposure value 0x%x\n", ctrl->val); + ret = ov8856_write_reg(ov8856->client, + OV8856_REG_EXPOSURE, + OV8856_REG_VALUE_24BIT, + ctrl->val << 4); + break; + case V4L2_CID_ANALOGUE_GAIN: + dev_dbg(&client->dev, "set analog gain value 0x%x\n", ctrl->val); + ret = ov8856_write_reg(ov8856->client, + OV8856_REG_GAIN_H, + OV8856_REG_VALUE_08BIT, + (ctrl->val >> OV8856_GAIN_H_SHIFT) & + OV8856_GAIN_H_MASK); + ret |= ov8856_write_reg(ov8856->client, + OV8856_REG_GAIN_L, + OV8856_REG_VALUE_08BIT, + ctrl->val & OV8856_GAIN_L_MASK); break; - case V4L2_CID_VBLANK: - ret = ov8856_write_reg(ov8856, OV8856_REG_VTS, - OV8856_REG_VALUE_16BIT, - ov8856->cur_mode->height + ctrl->val); + dev_info(&client->dev, "set vb value 0x%x\n", ctrl->val); + ret = ov8856_write_reg(ov8856->client, + OV8856_REG_VTS, + OV8856_REG_VALUE_16BIT, + ctrl->val + ov8856->cur_mode->height); break; - case V4L2_CID_TEST_PATTERN: - ret = ov8856_test_pattern(ov8856, ctrl->val); + ret = ov8856_enable_test_pattern(ov8856, ctrl->val); break; - case V4L2_CID_HFLIP: - ret = ov8856_set_ctrl_hflip(ov8856, ctrl->val); - break; + dev_info(&client->dev, "set hflip value 0x%x\n", ctrl->val); + ret = ov8856_read_reg(ov8856->client, OV8856_MIRROR_REG, + OV8856_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= MIRROR_BIT_MASK; + else + val &= ~MIRROR_BIT_MASK; + + + ret |= ov8856_write_reg(ov8856->client, OV8856_MIRROR_REG, + OV8856_REG_VALUE_08BIT, + val); + ret |= ov8856_read_reg(ov8856->client, OV8856_ISPCTRL2E_REG, + OV8856_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val &= ~(ISPCTRL2E_BIT_MASK); + else + val |= ISPCTRL2E_BIT_MASK; + + ret |= ov8856_write_reg(ov8856->client, OV8856_ISPCTRL2E_REG, + OV8856_REG_VALUE_08BIT, + val); + + ret |= ov8856_read_reg(ov8856->client, OV8856_ISPCTRL1_REG, + OV8856_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val |= ISPCTRL1_BIT_MASK; + else + val &= ~ISPCTRL1_BIT_MASK; + + ret |= ov8856_write_reg(ov8856->client, OV8856_ISPCTRL1_REG, + OV8856_REG_VALUE_08BIT, + val); + + break; case V4L2_CID_VFLIP: - ret = ov8856_set_ctrl_vflip(ov8856, ctrl->val); - break; + dev_info(&client->dev, "set vflip value 0x%x\n", ctrl->val); + ret = ov8856_read_reg(ov8856->client, OV8856_FLIP_REG, + OV8856_REG_VALUE_08BIT, + &val); + if (ctrl->val) + val &= ~(MIRROR_BIT_MASK); + else + val |= MIRROR_BIT_MASK; + ret |= ov8856_write_reg(ov8856->client, OV8856_FLIP_REG, + OV8856_REG_VALUE_08BIT, + val); + break; default: - ret = -EINVAL; + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); break; } @@ -1888,514 +1707,343 @@ static const struct v4l2_ctrl_ops ov8856_ctrl_ops = { .s_ctrl = ov8856_set_ctrl, }; -static int ov8856_init_controls(struct ov8856 *ov8856) +static int ov8856_initialize_controls(struct ov8856 *ov8856) { - struct v4l2_ctrl_handler *ctrl_hdlr; - s64 exposure_max, h_blank; + const struct ov8856_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; int ret; - ctrl_hdlr = &ov8856->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + handler = &ov8856->ctrl_handler; + mode = ov8856->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 8); if (ret) return ret; + handler->lock = &ov8856->mutex; - ctrl_hdlr->lock = &ov8856->mutex; - ov8856->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE - (ov8856->priv_lane->link_freq_menu_items) - - 1, - 0, ov8856->priv_lane->link_freq_menu_items); - if (ov8856->link_freq) - ov8856->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ, + 0, 0, link_freq_menu_items); + if (ctrl) + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - ov8856->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, - to_rate(ov8856->priv_lane->link_freq_menu_items, - 0, - ov8856->cur_mode->data_lanes), 1, - to_rate(ov8856->priv_lane->link_freq_menu_items, - 0, - ov8856->cur_mode->data_lanes)); - ov8856->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_VBLANK, - ov8856->cur_mode->vts_min - ov8856->cur_mode->height, - OV8856_VTS_MAX - ov8856->cur_mode->height, 1, - ov8856->cur_mode->vts_def - - ov8856->cur_mode->height); - h_blank = to_pixels_per_line(ov8856->priv_lane->link_freq_menu_items, - ov8856->cur_mode->hts, - ov8856->cur_mode->link_freq_index, - ov8856->cur_mode->data_lanes) - - ov8856->cur_mode->width; - ov8856->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_HBLANK, h_blank, h_blank, 1, - h_blank); + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, ov8856->pixel_rate, 1, ov8856->pixel_rate); + + h_blank = mode->hts_def - mode->width; + ov8856->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); if (ov8856->hblank) ov8856->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - OV8856_ANAL_GAIN_MIN, OV8856_ANAL_GAIN_MAX, - OV8856_ANAL_GAIN_STEP, OV8856_ANAL_GAIN_MIN); - v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, V4L2_CID_DIGITAL_GAIN, - OV8856_DGTL_GAIN_MIN, OV8856_DGTL_GAIN_MAX, - OV8856_DGTL_GAIN_STEP, OV8856_DGTL_GAIN_DEFAULT); - exposure_max = ov8856->cur_mode->vts_def - OV8856_EXPOSURE_MAX_MARGIN; - ov8856->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_EXPOSURE, - OV8856_EXPOSURE_MIN, exposure_max, - OV8856_EXPOSURE_STEP, - exposure_max); - v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(ov8856_test_pattern_menu) - 1, - 0, 0, ov8856_test_pattern_menu); - v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(ctrl_hdlr, &ov8856_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - if (ctrl_hdlr->error) - return ctrl_hdlr->error; + vblank_def = mode->vts_def - mode->height; + ov8856->vblank = v4l2_ctrl_new_std(handler, &ov8856_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + OV8856_VTS_MAX - mode->height, + 1, vblank_def); - ov8856->sd.ctrl_handler = ctrl_hdlr; + exposure_max = mode->vts_def - 6; + ov8856->exposure = v4l2_ctrl_new_std(handler, &ov8856_ctrl_ops, + V4L2_CID_EXPOSURE, OV8856_EXPOSURE_MIN, + exposure_max, OV8856_EXPOSURE_STEP, + mode->exp_def); + + ov8856->anal_gain = v4l2_ctrl_new_std(handler, &ov8856_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, OV8856_GAIN_MIN, + OV8856_GAIN_MAX, OV8856_GAIN_STEP, + OV8856_GAIN_DEFAULT); + + ov8856->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &ov8856_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov8856_test_pattern_menu) - 1, + 0, 0, ov8856_test_pattern_menu); + ov8856->h_flip = v4l2_ctrl_new_std(handler, &ov8856_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + ov8856->v_flip = v4l2_ctrl_new_std(handler, &ov8856_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + dev_err(&ov8856->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + ov8856->subdev.ctrl_handler = handler; return 0; -} -static void ov8856_update_pad_format(struct ov8856 *ov8856, - const struct ov8856_mode *mode, - struct v4l2_mbus_framefmt *fmt) -{ - int index; - - fmt->width = mode->width; - fmt->height = mode->height; - for (index = 0; index < ARRAY_SIZE(ov8856_mbus_codes); ++index) - if (ov8856_mbus_codes[index] == fmt->code) - break; - if (index == ARRAY_SIZE(ov8856_mbus_codes)) - index = mode->default_mbus_index; - fmt->code = ov8856_mbus_codes[index]; - ov8856->cur_mbus_index = index; - fmt->field = V4L2_FIELD_NONE; -} - -static int ov8856_start_streaming(struct ov8856 *ov8856) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - const struct ov8856_reg_list *reg_list; - int link_freq_index, ret; - - ret = ov8856_identify_module(ov8856); - if (ret) - return ret; - - link_freq_index = ov8856->cur_mode->link_freq_index; - reg_list = &ov8856->priv_lane->link_freq_configs[link_freq_index].reg_list; - - ret = ov8856_write_reg_list(ov8856, reg_list); - if (ret) { - dev_err(&client->dev, "failed to set plls"); - return ret; - } - - reg_list = &ov8856->cur_mode->reg_list; - ret = ov8856_write_reg_list(ov8856, reg_list); - if (ret) { - dev_err(&client->dev, "failed to set mode"); - return ret; - } - - reg_list = &bayer_offset_configs[ov8856->cur_mbus_index]; - ret = ov8856_write_reg_list(ov8856, reg_list); - if (ret) { - dev_err(&client->dev, "failed to set mbus format"); - return ret; - } - - ret = __v4l2_ctrl_handler_setup(ov8856->sd.ctrl_handler); - if (ret) - return ret; - - ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, - OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING); - if (ret) { - dev_err(&client->dev, "failed to set stream"); - return ret; - } - - return 0; -} - -static void ov8856_stop_streaming(struct ov8856 *ov8856) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - - if (ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, - OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream"); -} - -static int ov8856_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct ov8856 *ov8856 = to_ov8856(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - - if (ov8856->streaming == enable) - return 0; - - mutex_lock(&ov8856->mutex); - if (enable) { - ret = pm_runtime_resume_and_get(&client->dev); - if (ret < 0) { - mutex_unlock(&ov8856->mutex); - return ret; - } - - ret = ov8856_start_streaming(ov8856); - if (ret) { - enable = 0; - ov8856_stop_streaming(ov8856); - pm_runtime_put(&client->dev); - } - } else { - ov8856_stop_streaming(ov8856); - pm_runtime_put(&client->dev); - } - - ov8856->streaming = enable; - mutex_unlock(&ov8856->mutex); +err_free_handler: + v4l2_ctrl_handler_free(handler); return ret; } -static int __ov8856_power_on(struct ov8856 *ov8856) +static int ov8856_check_sensor_id(struct ov8856 *ov8856, + struct i2c_client *client) { - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); + struct device *dev = &ov8856->client->dev; + u32 id = 0; int ret; - if (is_acpi_node(dev_fwnode(&client->dev))) - return 0; + ret = ov8856_read_reg(client, OV8856_REG_CHIP_ID, + OV8856_REG_VALUE_24BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; + } - ret = clk_prepare_enable(ov8856->xvclk); - if (ret < 0) { - dev_err(&client->dev, "failed to enable xvclk\n"); + ret = ov8856_read_reg(client, OV8856_CHIP_REVISION_REG, + OV8856_REG_VALUE_08BIT, &id); + if (ret) { + dev_err(dev, "Read chip revision register error\n"); + return -ENODEV; + } + dev_info(dev, "Detected OV%06x sensor, REVISION 0x%x\n", CHIP_ID, id); + + if (ov8856->lane_num == 4) { + ov8856_global_regs = ov8856_global_regs_4lane; + ov8856->cur_mode = &supported_modes_4lane[0]; + supported_modes = supported_modes_4lane; + ov8856->cfg_num = ARRAY_SIZE(supported_modes_4lane); + } else { + ov8856_global_regs = ov8856_global_regs_2lane; + ov8856->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + ov8856->cfg_num = ARRAY_SIZE(supported_modes_2lane); + } + + return 0; +} + +static int ov8856_configure_regulators(struct ov8856 *ov8856) +{ + unsigned int i; + + for (i = 0; i < OV8856_NUM_SUPPLIES; i++) + ov8856->supplies[i].supply = ov8856_supply_names[i]; + + return devm_regulator_bulk_get(&ov8856->client->dev, + OV8856_NUM_SUPPLIES, + ov8856->supplies); +} + +static int ov8856_parse_of(struct ov8856 *ov8856) +{ + struct device *dev = &ov8856->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + ov8856->lane_num = rval; + if (ov8856->lane_num == 4) { + ov8856->cur_mode = &supported_modes_4lane[0]; + supported_modes = supported_modes_4lane; + ov8856->cfg_num = ARRAY_SIZE(supported_modes_4lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov8856->pixel_rate = MIPI_FREQ * 2U * ov8856->lane_num / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + ov8856->lane_num, ov8856->pixel_rate); + } else { + ov8856->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + ov8856->cfg_num = ARRAY_SIZE(supported_modes_2lane); + + /*pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + ov8856->pixel_rate = MIPI_FREQ * 2U * (ov8856->lane_num) / 10U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + ov8856->lane_num, ov8856->pixel_rate); + } + return 0; +} + +static int ov8856_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct ov8856 *ov8856; + struct v4l2_subdev *sd; + char facing[2]; + int ret; + struct device_node *eeprom_ctrl_node; + struct i2c_client *eeprom_ctrl_client; + struct v4l2_subdev *eeprom_ctrl; + struct otp_info *otp_ptr; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + ov8856 = devm_kzalloc(dev, sizeof(*ov8856), GFP_KERNEL); + if (!ov8856) + return -ENOMEM; + + ov8856->client = client; + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &ov8856->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &ov8856->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &ov8856->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &ov8856->len_name); + if (ret) { + dev_err(dev, + "could not get module information!\n"); + return -EINVAL; + } + + ov8856->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(ov8856->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + ov8856->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ov8856->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + ov8856->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ov8856->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + ov8856->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(ov8856->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios, maybe no use\n"); + + ret = ov8856_configure_regulators(ov8856); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); return ret; } - if (ov8856->reset_gpio) { - gpiod_set_value_cansleep(ov8856->reset_gpio, 1); - usleep_range(1000, 2000); + ret = ov8856_parse_of(ov8856); + if (ret != 0) + return -EINVAL; + + ov8856->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(ov8856->pinctrl)) { + ov8856->pins_default = + pinctrl_lookup_state(ov8856->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(ov8856->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + ov8856->pins_sleep = + pinctrl_lookup_state(ov8856->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(ov8856->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); } - ret = regulator_bulk_enable(ARRAY_SIZE(ov8856_supply_names), - ov8856->supplies); - if (ret < 0) { - dev_err(&client->dev, "failed to enable regulators\n"); - goto disable_clk; + mutex_init(&ov8856->mutex); + + sd = &ov8856->subdev; + v4l2_i2c_subdev_init(sd, client, &ov8856_subdev_ops); + ret = ov8856_initialize_controls(ov8856); + if (ret) + goto err_destroy_mutex; + + ret = __ov8856_power_on(ov8856); + if (ret) + goto err_free_handler; + + ret = ov8856_check_sensor_id(ov8856, client); + if (ret) + goto err_power_off; + + eeprom_ctrl_node = of_parse_phandle(node, "eeprom-ctrl", 0); + if (eeprom_ctrl_node) { + eeprom_ctrl_client = + of_find_i2c_device_by_node(eeprom_ctrl_node); + of_node_put(eeprom_ctrl_node); + if (IS_ERR_OR_NULL(eeprom_ctrl_client)) { + dev_err(dev, "can not get node\n"); + goto continue_probe; + } + eeprom_ctrl = i2c_get_clientdata(eeprom_ctrl_client); + if (IS_ERR_OR_NULL(eeprom_ctrl)) { + dev_err(dev, "can not get eeprom i2c client\n"); + } else { + otp_ptr = devm_kzalloc(dev, sizeof(*otp_ptr), GFP_KERNEL); + if (!otp_ptr) + return -ENOMEM; + ret = v4l2_subdev_call(eeprom_ctrl, + core, ioctl, 0, otp_ptr); + if (!ret) { + ov8856->otp = otp_ptr; + } else { + ov8856->otp = NULL; + devm_kfree(dev, otp_ptr); + dev_warn(dev, "can not get otp info, skip!\n"); + } + } } - gpiod_set_value_cansleep(ov8856->reset_gpio, 0); - usleep_range(1500, 1800); +continue_probe: +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &ov8856_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + ov8856->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &ov8856->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(ov8856->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + ov8856->module_index, facing, + OV8856_NAME, dev_name(sd->dev)); + ret = v4l2_async_register_subdev_sensor(sd); + if (ret) { + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); + + dev_info(dev, "%s success!", __func__); return 0; -disable_clk: - gpiod_set_value_cansleep(ov8856->reset_gpio, 1); - clk_disable_unprepare(ov8856->xvclk); - - return ret; -} - -static void __ov8856_power_off(struct ov8856 *ov8856) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov8856->sd); - - if (is_acpi_node(dev_fwnode(&client->dev))) - return; - - gpiod_set_value_cansleep(ov8856->reset_gpio, 1); - regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names), - ov8856->supplies); - clk_disable_unprepare(ov8856->xvclk); -} - -static int __maybe_unused ov8856_suspend(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov8856 *ov8856 = to_ov8856(sd); - - mutex_lock(&ov8856->mutex); - if (ov8856->streaming) - ov8856_stop_streaming(ov8856); - +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: __ov8856_power_off(ov8856); - mutex_unlock(&ov8856->mutex); - - return 0; -} - -static int __maybe_unused ov8856_resume(struct device *dev) -{ - struct v4l2_subdev *sd = dev_get_drvdata(dev); - struct ov8856 *ov8856 = to_ov8856(sd); - int ret; - - mutex_lock(&ov8856->mutex); - - __ov8856_power_on(ov8856); - if (ov8856->streaming) { - ret = ov8856_start_streaming(ov8856); - if (ret) { - ov8856->streaming = false; - ov8856_stop_streaming(ov8856); - mutex_unlock(&ov8856->mutex); - return ret; - } - } - - mutex_unlock(&ov8856->mutex); - - return 0; -} - -static int ov8856_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct ov8856 *ov8856 = to_ov8856(sd); - const struct ov8856_mode *mode; - s32 vblank_def, h_blank; - - mode = v4l2_find_nearest_size(ov8856->priv_lane->supported_modes, - ov8856->modes_size, - width, height, fmt->format.width, - fmt->format.height); - - mutex_lock(&ov8856->mutex); - ov8856_update_pad_format(ov8856, mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, sd_state, fmt->pad) = fmt->format; - } else { - ov8856->cur_mode = mode; - __v4l2_ctrl_s_ctrl(ov8856->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(ov8856->pixel_rate, - to_rate(ov8856->priv_lane->link_freq_menu_items, - mode->link_freq_index, - ov8856->cur_mode->data_lanes)); - - /* Update limits and set FPS to default */ - vblank_def = mode->vts_def - mode->height; - __v4l2_ctrl_modify_range(ov8856->vblank, - mode->vts_min - mode->height, - OV8856_VTS_MAX - mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(ov8856->vblank, vblank_def); - h_blank = to_pixels_per_line(ov8856->priv_lane->link_freq_menu_items, - mode->hts, - mode->link_freq_index, - ov8856->cur_mode->data_lanes) - - mode->width; - __v4l2_ctrl_modify_range(ov8856->hblank, h_blank, h_blank, 1, - h_blank); - } - - mutex_unlock(&ov8856->mutex); - - return 0; -} - -static int ov8856_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct ov8856 *ov8856 = to_ov8856(sd); - - mutex_lock(&ov8856->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&ov8856->sd, - sd_state, - fmt->pad); - else - ov8856_update_pad_format(ov8856, ov8856->cur_mode, &fmt->format); - - mutex_unlock(&ov8856->mutex); - - return 0; -} - -static int ov8856_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(ov8856_mbus_codes)) - return -EINVAL; - - code->code = ov8856_mbus_codes[code->index]; - - return 0; -} - -static int ov8856_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - struct ov8856 *ov8856 = to_ov8856(sd); - int index; - - if (fse->index >= ov8856->modes_size) - return -EINVAL; - - for (index = 0; index < ARRAY_SIZE(ov8856_mbus_codes); ++index) - if (fse->code == ov8856_mbus_codes[index]) - break; - if (index == ARRAY_SIZE(ov8856_mbus_codes)) - return -EINVAL; - - fse->min_width = ov8856->priv_lane->supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = ov8856->priv_lane->supported_modes[fse->index].height; - fse->max_height = fse->min_height; - - return 0; -} - -static int ov8856_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct ov8856 *ov8856 = to_ov8856(sd); - - mutex_lock(&ov8856->mutex); - ov8856_update_pad_format(ov8856, &ov8856->priv_lane->supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->state, 0)); - mutex_unlock(&ov8856->mutex); - - return 0; -} - -static const struct v4l2_subdev_video_ops ov8856_video_ops = { - .s_stream = ov8856_set_stream, -}; - -static const struct v4l2_subdev_pad_ops ov8856_pad_ops = { - .set_fmt = ov8856_set_format, - .get_fmt = ov8856_get_format, - .enum_mbus_code = ov8856_enum_mbus_code, - .enum_frame_size = ov8856_enum_frame_size, -}; - -static const struct v4l2_subdev_ops ov8856_subdev_ops = { - .video = &ov8856_video_ops, - .pad = &ov8856_pad_ops, -}; - -static const struct media_entity_operations ov8856_subdev_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_internal_ops ov8856_internal_ops = { - .open = ov8856_open, -}; - - -static int ov8856_get_hwcfg(struct ov8856 *ov8856, struct device *dev) -{ - struct fwnode_handle *ep; - struct fwnode_handle *fwnode = dev_fwnode(dev); - struct v4l2_fwnode_endpoint bus_cfg = { - .bus_type = V4L2_MBUS_CSI2_DPHY - }; - u32 xvclk_rate; - int ret; - unsigned int i, j; - - if (!fwnode) - return -ENXIO; - - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &xvclk_rate); - if (ret) - return ret; - - if (!is_acpi_node(fwnode)) { - ov8856->xvclk = devm_clk_get(dev, "xvclk"); - if (IS_ERR(ov8856->xvclk)) { - dev_err(dev, "could not get xvclk clock (%pe)\n", - ov8856->xvclk); - return PTR_ERR(ov8856->xvclk); - } - - clk_set_rate(ov8856->xvclk, xvclk_rate); - xvclk_rate = clk_get_rate(ov8856->xvclk); - - ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset", - GPIOD_OUT_LOW); - if (IS_ERR(ov8856->reset_gpio)) - return PTR_ERR(ov8856->reset_gpio); - - for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++) - ov8856->supplies[i].supply = ov8856_supply_names[i]; - - ret = devm_regulator_bulk_get(dev, - ARRAY_SIZE(ov8856_supply_names), - ov8856->supplies); - if (ret) - return ret; - } - - if (xvclk_rate != OV8856_XVCLK_19_2) - dev_warn(dev, "external clock rate %u is unsupported", - xvclk_rate); - - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; - - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - fwnode_handle_put(ep); - if (ret) - return ret; - - /* Get number of data lanes */ - if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2 && - bus_cfg.bus.mipi_csi2.num_data_lanes != 4) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", - bus_cfg.bus.mipi_csi2.num_data_lanes); - ret = -EINVAL; - goto check_hwcfg_error; - } - - dev_dbg(dev, "Using %u data lanes\n", ov8856->cur_mode->data_lanes); - - if (bus_cfg.bus.mipi_csi2.num_data_lanes == 2) - ov8856->priv_lane = &lane_cfg_2; - else - ov8856->priv_lane = &lane_cfg_4; - - ov8856->modes_size = ov8856_modes_num(ov8856); - - if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); - ret = -EINVAL; - goto check_hwcfg_error; - } - - for (i = 0; i < ARRAY_SIZE(ov8856->priv_lane->link_freq_menu_items); i++) { - for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { - if (ov8856->priv_lane->link_freq_menu_items[i] == - bus_cfg.link_frequencies[j]) - break; - } - - if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", - ov8856->priv_lane->link_freq_menu_items[i]); - ret = -EINVAL; - goto check_hwcfg_error; - } - } - -check_hwcfg_error: - v4l2_fwnode_endpoint_free(&bus_cfg); +err_free_handler: + v4l2_ctrl_handler_free(&ov8856->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&ov8856->mutex); return ret; } @@ -2406,129 +2054,55 @@ static void ov8856_remove(struct i2c_client *client) struct ov8856 *ov8856 = to_ov8856(sd); v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); - v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); - mutex_destroy(&ov8856->mutex); - - __ov8856_power_off(ov8856); -} - -static int ov8856_probe(struct i2c_client *client) -{ - struct ov8856 *ov8856; - int ret; - bool full_power; - - ov8856 = devm_kzalloc(&client->dev, sizeof(*ov8856), GFP_KERNEL); - if (!ov8856) - return -ENOMEM; - - ret = ov8856_get_hwcfg(ov8856, &client->dev); - if (ret) { - dev_err(&client->dev, "failed to get HW configuration: %d", - ret); - return ret; - } - - v4l2_i2c_subdev_init(&ov8856->sd, client, &ov8856_subdev_ops); - - full_power = acpi_dev_state_d0(&client->dev); - if (full_power) { - ret = __ov8856_power_on(ov8856); - if (ret) { - dev_err(&client->dev, "failed to power on\n"); - return ret; - } - - ret = ov8856_identify_module(ov8856); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); - goto probe_power_off; - } - } - - mutex_init(&ov8856->mutex); - ov8856->cur_mode = &ov8856->priv_lane->supported_modes[0]; - ov8856->cur_mbus_index = ov8856->cur_mode->default_mbus_index; - ret = ov8856_init_controls(ov8856); - if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); - goto probe_error_v4l2_ctrl_handler_free; - } - - ov8856->sd.internal_ops = &ov8856_internal_ops; - ov8856->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - ov8856->sd.entity.ops = &ov8856_subdev_entity_ops; - ov8856->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - ov8856->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&ov8856->sd.entity, 1, &ov8856->pad); - if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); - goto probe_error_v4l2_ctrl_handler_free; - } - - ret = v4l2_async_register_subdev_sensor(&ov8856->sd); - if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", - ret); - goto probe_error_media_entity_cleanup; - } - - /* Set the device's state to active if it's in D0 state. */ - if (full_power) - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); - - return 0; - -probe_error_media_entity_cleanup: - media_entity_cleanup(&ov8856->sd.entity); - -probe_error_v4l2_ctrl_handler_free: - v4l2_ctrl_handler_free(ov8856->sd.ctrl_handler); - mutex_destroy(&ov8856->mutex); - -probe_power_off: - __ov8856_power_off(ov8856); - - return ret; -} - -static const struct dev_pm_ops ov8856_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ov8856_suspend, ov8856_resume) -}; - -#ifdef CONFIG_ACPI -static const struct acpi_device_id ov8856_acpi_ids[] = { - {"OVTI8856"}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, ov8856_acpi_ids); #endif + v4l2_ctrl_handler_free(&ov8856->ctrl_handler); + kfree(ov8856->otp); + mutex_destroy(&ov8856->mutex); + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __ov8856_power_off(ov8856); + pm_runtime_set_suspended(&client->dev); +} + +#if IS_ENABLED(CONFIG_OF) static const struct of_device_id ov8856_of_match[] = { { .compatible = "ovti,ov8856" }, - { /* sentinel */ } + {}, }; MODULE_DEVICE_TABLE(of, ov8856_of_match); +#endif + +static const struct i2c_device_id ov8856_match_id[] = { + { "ovti,ov8856", 0 }, + { }, +}; static struct i2c_driver ov8856_i2c_driver = { .driver = { - .name = "ov8856", + .name = OV8856_NAME, .pm = &ov8856_pm_ops, - .acpi_match_table = ACPI_PTR(ov8856_acpi_ids), - .of_match_table = ov8856_of_match, + .of_match_table = of_match_ptr(ov8856_of_match), }, - .probe_new = ov8856_probe, - .remove = ov8856_remove, - .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, + .probe = &ov8856_probe, + .remove = &ov8856_remove, + .id_table = ov8856_match_id, }; -module_i2c_driver(ov8856_i2c_driver); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&ov8856_i2c_driver); +} -MODULE_AUTHOR("Ben Kao "); -MODULE_DESCRIPTION("OmniVision OV8856 sensor driver"); +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&ov8856_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("OmniVision ov8856 sensor driver"); MODULE_LICENSE("GPL v2");