@alb702 在 V853 SDK : PMU TWI 中说:
[267]ic cant match axp, please check...
V853 和 V853s 的芯片安全系统验证不一样,SDK不能通用,这行输出表示芯片型号验证失败,跳过初始化DRAM
@alb702 在 V853 SDK : PMU TWI 中说:
[267]ic cant match axp, please check...
V853 和 V853s 的芯片安全系统验证不一样,SDK不能通用,这行输出表示芯片型号验证失败,跳过初始化DRAM
@kanken6174 不建议编译gcc进入固件,首先这个gcc没有适配平台,其次v851s的64M内存也无法支持gcc的使用
helloworld_fel 使用了 VFP 寄存器参数,但是某个库文件(libgcc.a(_udivmoddi4.o))却不支持。
缺少 .note.GNU-stack 段,暗示可执行栈缺失。
对于具有 RWX 权限的 LOAD 段,给出了警告。
在链接时,出现了对 raise 函数的未定义引用。
内核版本不匹配,请更换Ubuntu内核版本到5.15.y,目前你是6.5
(1)在线模式:四个vipp和dma实体,最大缩小比例为16*16,每路最多可支持16个orl
(2)离线模式:每个vipp和dma可分时复用为4个vipp和dma虚拟体,四个vipp和dma实体相互独立,在线模式和离线模式开关也是相互独立的;
(3)VIPP和DAM的分时复用(离线模式)与isp和tdm的分时复用(离线模式)是绑定关系,即tdm和isp开启了离线模式,vipp和dma的输入端如果是isp,那么vipp和dma也需要开启离线模式;
(4)只有VIPP0和dma0实体支持VE在线编码,而vipp0在线,如果vipp00的输入端为isp,那么tdm和isp也只能配置在线模式,而isp在线,那么四个vipp和dma实体都只能配置在线模式;
online 和 offline 配 置 方 式 在 board.dts , 所 以 需 要 在 对 应 版 型 的 board.dts 中 找 到 vind0 节 点 配 置 列 表 , 对 应 关 系 为 tdm 对 应 节 点 , isp 对 应 isp00 节 点 , vipp 对 应 scaler00 、 scaler10 、 scaler20 和 scaler30 节 点 , dma 对 , 应 vinc00 、 vinc10 、 vinc20 和 vinc30 节 点 。
环境没有安装
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install build-essential subversion git libncurses5-dev zlib1g-dev gawk flex bison quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip lsof
sudo apt-get install kconfig-frontends android-tools-mkbootimg python2 libpython3-dev
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt install gcc-multilib
sudo apt install libc6:i386 libstdc++6:i386 lib32z1
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install build-essential subversion git libncurses5-dev zlib1g-dev gawk flex bison quilt libssl-dev xsltproc libxml-parser-perl mercurial bzr ecj cvs unzip lsof
sudo apt-get install android-tools-mkbootimg libpython3-dev
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt install gcc-multilib
sudo apt install libc6:i386 libstdc++6:i386 lib32z1
pacman -Syyuu
pacman -S --needed base-devel autoconf automake bash binutils bison bzip2 fakeroot file findutils flex gawk gcc gettext git grep groff gzip time unzip util-linux wget which zlib asciidoc help2man intltool perl-extutils-makemaker swig
pacman -S --needed libelf libtool libxslt m4 make ncurses openssl patch pkgconf python rsync sed texinfo
pacman -S --needed multilib-devel
sudo dnf --setopt install_weak_deps=False --skip-broken install bash-completion bzip2 gcc gcc-c++ git make ncurses-devel patch rsync tar unzip wget which diffutils python2 python3 perl-base perl-Data-Dumper perl-File-Compare perl-File-Copy perl-FindBin perl-Thread-Queue glibc.i686
sudo zypper install --no-recommends asciidoc bash bc binutils bzip2 fastjar flex gawk gcc
找到 env.cfg
#kernel command arguments
earlyprintk=sunxi-uart,0x02500000
initcall_debug=0
console=ttyS0,115200
consolefb=tty0
nand_root=ubi0_4
mmc_root=/dev/mmcblk0p4
nor_root=/dev/mtdblock1
init=/init
loglevel=8
coherent_pool=16K
#reserve_list=30M@64M,78M@128M,200M@512M
mac=
wifi_mac=
bt_mac=
specialstr=
root_partition=rootfs
mtd_name=sys
rootfstype=ubifs, rw
#set kernel cmdline if boot.img or recovery.img has no cmdline we will use this
setargs_nor=setenv bootargs earlyprintk=${earlyprintk} clk_ignore_unused initcall_debug=${initcall_debug} console=${console} console=${console—fb} loglevel=${loglevel} root=${nor_root} rootwait init=${init} rdinit=${rdinit} partitions=${partitions} cma=${cma} coherent_pool=${coherent_pool} ion_carveout_list=${reserve_list}
setargs_nand=setenv bootargs earlyprintk=${earlyprintk} clk_ignore_unused initcall_debug=${initcall_debug} console=${console} console=${console—fb} loglevel=${loglevel} ubi.mtd=${mtd_name} root=${nand_root} rootfstype=${rootfstype} rootwait init=${init} rdinit=${rdinit} partitions=${partitions} cma=${cma} mac_addr=${mac} wifi_mac=${wifi_mac} bt_mac=${bt_mac} selinux=${selinux} specialstr=${specialstr} coherent_pool=${coherent_pool} ion_carveout_list=${reserve_list}
setargs_nand_ubi=setenv bootargs ubi.mtd=${mtd_name} earlyprintk=${earlyprintk} clk_ignore_unused initcall_debug=${initcall_debug} console=${console} console=${console—fb} loglevel=${loglevel} root=${nand_root} rootfstype=${rootfstype} init=${init} partitions=${partitions} cma=${cma} snum=${snum} mac_addr=${mac} wifi_mac=${wifi_mac} bt_mac=${bt_mac} specialstr=${specialstr} gpt=1
setargs_mmc=setenv bootargs earlyprintk=${earlyprintk} clk_ignore_unused initcall_debug=${initcall_debug} console=${console} console=${console—fb} loglevel=${loglevel} root=${mmc_root} rootwait init=${init} partitions=${partitions} cma=${cma} mac_addr=${mac} wifi_mac=${wifi_mac} bt_mac=${bt_mac} selinux=${selinux} specialstr=${specialstr} coherent_pool=${coherent_pool} ion_carveout_list=${reserve_list}
#nand command syntax: sunxi_flash read address partition_name read_bytes
#0x4007f800 = 0x40080000(kernel entry) - 0x800(boot.img header 2k)
boot_partition=boot
boot_normal=sunxi_flash read 44800000 ${boot_partition};bootm 44800000
boot_recovery=sunxi_flash read 44800000 extend;bootm 44800000
boot_fastboot=fastboot
#recovery key
recovery_key_value_max=0x13
recovery_key_value_min=0x10
#fastboot key
fastboot_key_value_max=0x8
fastboot_key_value_min=0x2
#uboot system env config
bootdelay=1
#default bootcmd, will change at runtime according to key press
bootcmd=run setargs_nand boot_normal#default nand boot
#verify the kernel
verify=N
@yunyisa 自制的固件无法打包成全志的镜像,只能由SDK生成,全志镜像有专有的封装格式,不仅仅是地址偏移
使用APST量产工具下载,APST下载地址https://open.allwinnertech.com/
使用 V3s SDK 提供VFE框架的驱动移植到VIN框架下抓图成功。但是图像非常暗,并且撕裂,抓raw数据查看也是一样,考虑可能mclk不同步导致。
sensor0:sensor@0 {
device_type = "sensor0";
sensor0_mname = "ov5648_mipi";
sensor0_twi_cci_id = <0>;
sensor0_twi_addr = <0x6c>;
sensor0_mclk_id = <0>;
sensor0_pos = "rear";
sensor0_isp_used = <1>;
sensor0_fmt = <1>;
sensor0_stby_mode = <0>;
sensor0_vflip = <0>;
sensor0_hflip = <0>;
sensor0_iovdd-supply = <>;
sensor0_iovdd_vol = <1800000>;
sensor0_avdd-supply = <>;
sensor0_avdd_vol = <2800000>;
sensor0_dvdd-supply = <>;
sensor0_dvdd_vol = <1200000>;
sensor0_power_en = <>;
sensor0_reset = <&pio PA 18 1 0 1 0>;
sensor0_pwdn = <&pio PA 19 1 0 1 0>;
sensor0_sm_hs = <>;
sensor0_sm_vs = <>;
flash_handle = <>;
act_handle = <>;
status = "okay";
};
/*
* A V4L2 driver for ov5647 Raw cameras.
*
* Copyright (c) 2022 by YuzukiTsuru <gloomyghost@gloomyghost.com>
* Copyright (c) 2018 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Authors: Zheng ZeQun <zequnzheng@allwinnertech.com>
* Liang WeiJie <liangweijie@allwinnertech.com>
* YuzukiTsuru <gloomyghost@gloomyghost.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/clk.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <linux/io.h>
#include "camera.h"
#include "sensor_helper.h"
MODULE_AUTHOR("YuzukiTsuru");
MODULE_DESCRIPTION("A low-level driver for ov5647 sensors");
MODULE_LICENSE("GPL");
#define MCLK (24*1000*1000)
#define V4L2_IDENT_SENSOR 0x5648
/*
* Our nominal (default) frame rate.
*/
#define SENSOR_FRAME_RATE 30
/*
* The GC0310 i2c address
*/
#define I2C_ADDR 0x6c
#define SENSOR_NAME "ov5648_mipi"
/*
* The default register settings
*/
static struct regval_list sensor_default_regs[] = {
//Slave_ID=0x6c;
{0x0100, 0x00},// ; software standby
{0x0103, 0x01},// ; software reset
{REG_DLY, 0x25},
{0x370c, 0x03},// ; analog control
{0x5000, 0x06},// ; lens off, bpc on, wpc on
{0x5003, 0x08},// ; buf_en
{0x5a00, 0x08},//
{0x3000, 0xff},// ; D[9:8] output
{0x3001, 0xff},// ; D[7:0] output
{0x3002, 0xff},// ; Vsync, Href, PCLK, Frex, Strobe, SDA, GPIO1, GPIO0 output
{0x301d, 0xf0},//
{0x3a18, 0x00},// ; gain ceiling = 15.5x
{0x3a19, 0xf8},// ; gain ceiling
{0x3c01, 0x80},// ; band detection manual mode
{0x3b07, 0x0c},// ; strobe frex mode
//; analog control
{0x3630, 0x2e},
{0x3632, 0xe2},
{0x3633, 0x23},
{0x3634, 0x44},
{0x3620, 0x64},
{0x3621, 0xe0},
{0x3600, 0x37},
{0x3704, 0xa0},
{0x3703, 0x5a},
{0x3715, 0x78},
{0x3717, 0x01},
{0x3731, 0x02},
{0x370b, 0x60},
{0x3705, 0x1a},
{0x3f05, 0x02},
{0x3f06, 0x10},
{0x3f01, 0x0a},
//; AG/AE target
{0x3a0f, 0x58},// ; stable in high
{0x3a10, 0x50},// ; stable in low
{0x3a1b, 0x58},// ; stable out high
{0x3a1e, 0x50},// ; stable out low
{0x3a11, 0x60},// ; fast zone high
{0x3a1f, 0x28},// ; fast zone low
{0x4001, 0x02},// ; BLC start line
{0x4000, 0x09},// ; BLC enable
{0x3000, 0x00},// ; D[9:8] input
{0x3001, 0x00},// ; D[7:0] input
{0x3002, 0x00},// ; Vsync, Href, PCLK, Frex, Strobe, SDA, GPIO1, GPIO0 input
{0x3017, 0xe0},// ; MIPI PHY
{0x301c, 0xfc},//
{0x3636, 0x06},// ; analog control
{0x3016, 0x08},// ; MIPI pad enable
{0x3827, 0xec},//
{0x3018, 0x44},// ; MIPI 2 lane, MIPI enable
{0x3035, 0x21},// ; PLL
{0x3106, 0xf5},// ; PLL
{0x3034, 0x1a},// ; PLL
{0x301c, 0xf8},//
{0x3503, 0x03},// ; Gain has no latch delay, AGC manual, AEC
{0x3501, 0x10},// ; exposure[15:8]
{0x3502, 0x80},// ; exposure[7:0]
{0x350a, 0x00},// ; gain[9:8]
{0x350b, 0x7f},// ; gain[7:0]
{0x5001, 0x01},// ; AWB on
{0x5180, 0x08},// ; AWB manual gain enable
{0x5186, 0x04},// ; manual red gain high
{0x5187, 0x00},// ; manual red gain low
{0x5188, 0x04},// ; manual green gain high
{0x5189, 0x00},// ; manual green gain low
{0x518a, 0x04},// ; manual blue gain high
{0x518b, 0x00},// ; manual blue gain low
{0x5000, 0x06},// ; lenc off, bpc on, wpc on
};
static struct regval_list sensor_qsxga_regs[] = { //qsxga: 2592*1936@15fps
{0x0100, 0x00},// ; software standby
{0x3035, 0x21},// ; PLL
{0x3036, 0x66},// ; PLL
{0x303c, 0x11},// ; PLL
{0x3821, 0x06},// ; ISP mirror on, Sensor mirror on
{0x3820, 0x00},// ; ISP flip off, Sensor flip off
{0x3612, 0x5b},// ; analog control
{0x3618, 0x04},// ; analog control
{0x380c, 0x0a},// ; HTS = 2752
{0x380d, 0xc0},// ; HTS
{0x380e, 0x07},// ; VTS = 1974
{0x380f, 0xb6},// ; VTS
{0x3814, 0x11},// ; X INC
{0x3815, 0x11},// ; X INC
{0x3708, 0x64},// ; analog control
{0x3709, 0x12},// ; analog control
{0x3808, 0x0a},// ; X OUTPUT SIZE = 2592
{0x3809, 0x20},// ; X OUTPUT SIZE
{0x380a, 0x07},// ; Y OUTPUT SIZE = 1944
{0x380b, 0x98},// ; Y OUTPUT SIZE
{0x3800, 0x00},// ; X Start
{0x3801, 0x0c},// ; X Start
{0x3802, 0x00},// ; Y Start
{0x3803, 0x02},// ; Y Start
{0x3804, 0x0a},// ; X End
{0x3805, 0x33},// ; X End
{0x3806, 0x07},// ; Y End
{0x3807, 0xa1},// ; Y End
///////////; Banding filter
{0x3a08, 0x01},// ; B50
{0x3a09, 0x28},// ; B50
{0x3a0a, 0x00},// ; B60
{0x3a0b, 0xf6},// ; B60
{0x3a0d, 0x07},// ; B60 max
{0x3a0e, 0x06},// ; B50 max
{0x4004, 0x04},// ; black line number
{0x4837, 0x19},// ; MIPI pclk period
{0x0100, 0x01},// ; wake up from software standby
};
static struct regval_list sensor_720p_regs[] = { //720: 1280*720@30fps
{0x0100, 0x00},// ; software standby
{0x3035, 0x21},// ; PLL
{0x3036, 0x46},// ; PLL
{0x303c, 0x11},// ; PLL
{0x3821, 0x07},// ; ISP mirror on, Sensor mirror on, bin on
{0x3820, 0x41},// ; ISP flip off, Sensor flip off, bin on
{0x3612, 0x59},// ; analog control
{0x3618, 0x00},// ; analog control
{0x380c, 0x07},// ; HTS = 1896
{0x380d, 0x68},// ; HTS
{0x380e, 0x03},// ; VTS = 984
{0x380f, 0xd8},// ; VTS
{0x3814, 0x31},// ; X INC
{0x3815, 0x31},// ; Y INC
{0x3708, 0x64},// ; analog control
{0x3709, 0x52},// ; analog control
{0x3808, 0x05},// ; X OUTPUT SIZE = 1280
{0x3809, 0x00},// ; X OUTPUT SIZE
{0x380a, 0x03},// ; Y OUTPUT SIZE = 960
{0x380b, 0xc0},// ; Y OUTPUT SIZE
{0x3800, 0x00},// ; X Start
{0x3801, 0x18},// ; X Start
{0x3802, 0x00},// ; Y Start
{0x3803, 0x0e},// ; Y Start
{0x3804, 0x0a},// ; X End
{0x3805, 0x27},// ; X End
{0x3806, 0x07},// ; Y End
{0x3807, 0x95},// ; Y End
// banding filter
{0x3a08, 0x01},// ; B50
{0x3a09, 0x27},// ; B50
{0x3a0a, 0x00},// ; B60
{0x3a0b, 0xf6},// ; B60
{0x3a0d, 0x04},// ; B50 max
{0x3a0e, 0x03},// ; B60 max
{0x4004, 0x02},// ; black line number
{0x4837, 0x24},// ; MIPI pclk period
{0x0100, 0x01},// ; wake up from software standby
};
static struct regval_list sensor_fmt_raw[] = {
};
/*
* Code for dealing with controls.
* fill with different sensor module
* different sensor module has different settings here
* if not support the follow function ,retrun -EINVAL
*/
static int sensor_g_exp(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->exp;
sensor_print("sensor_get_exposure = %d\n", info->exp);
return 0;
}
static int sensor_s_exp(struct v4l2_subdev *sd, unsigned int exp_val)
{
unsigned char explow, expmid, exphigh;
struct sensor_info *info = to_state(sd);
if(exp_val>0xfffff)
exp_val=0xfffff;
sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x3503, 0x13);
exphigh = (unsigned char) ( (0x0f0000&exp_val)>>16);
expmid = (unsigned char) ( (0x00ff00&exp_val)>>8);
explow = (unsigned char) ( (0x0000ff&exp_val) );
//sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x3502, explow);
sensor_write(sd, 0x3501, expmid);
sensor_write(sd, 0x3500, exphigh);
sensor_write(sd, 0x3208, 0x10);//end group write
sensor_write(sd, 0x3208, 0xa0);//init group write
sensor_print("ov5647_mipi sensor_set_exp = %d, Done!\n", exp_val);
info->exp = exp_val;
return 0;
}
static int sensor_g_gain(struct v4l2_subdev *sd, __s32 *value)
{
struct sensor_info *info = to_state(sd);
*value = info->gain;
sensor_print("sensor_get_gain = %d\n", info->gain);
return 0;
}
static int sensor_s_gain(struct v4l2_subdev *sd, unsigned int gain_val)
{
struct sensor_info *info = to_state(sd);
unsigned char gainlow=0;
unsigned char gainhigh=0;
if(gain_val<1*16)
gain_val=16;
if(gain_val>64*16-1)
gain_val=64*16-1;
gainlow=(unsigned char)(gain_val&0xff);
gainhigh=(unsigned char)((gain_val>>8)&0x3);
sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x3503, 0x13);
sensor_write(sd, 0x350b, gainlow);
sensor_write(sd, 0x350a, gainhigh);
sensor_write(sd, 0x3208, 0x10);//end group write
sensor_write(sd, 0x3208, 0xa0);//init group write
//printk("ov5647_mipi sensor_set_gain = %d, Done!\n", gain_val);
info->gain = gain_val;
return 0;
}
static int ov5648_sensor_vts;
static int sensor_s_exp_gain(struct v4l2_subdev *sd,
struct sensor_exp_gain *exp_gain)
{
int exp_val, gain_val,frame_length,shutter;
unsigned char explow=0,expmid=0,exphigh=0;
unsigned char gainlow=0,gainhigh=0;
struct sensor_info *info = to_state(sd);
exp_val = exp_gain->exp_val;
gain_val = exp_gain->gain_val;
if(gain_val<1*16)
gain_val=16;
if(gain_val>64*16-1)
gain_val=64*16-1;
if(exp_val>0xfffff)
exp_val=0xfffff;
gainlow=(unsigned char)(gain_val&0xff);
gainhigh=(unsigned char)((gain_val>>8)&0x3);
exphigh = (unsigned char) ( (0x0f0000&exp_val)>>16);
expmid = (unsigned char) ( (0x00ff00&exp_val)>>8);
explow = (unsigned char) ( (0x0000ff&exp_val) );
shutter = exp_val/16;
sensor_print("ov5648_sensor_vts = %d\n",ov5648_sensor_vts);
if(shutter > ov5648_sensor_vts- 4)
frame_length = shutter + 4;
else
frame_length = ov5648_sensor_vts;
sensor_write(sd, 0x3503, 0x07);
sensor_write(sd, 0x380f, (frame_length & 0xff));
sensor_write(sd, 0x380e, (frame_length >> 8));
sensor_print("exp_val = %d,gain_val = %d\n",exp_val,gain_val);
sensor_write(sd, 0x3208, 0x00);//enter group write
sensor_write(sd, 0x350b, gainlow);
sensor_write(sd, 0x350a, gainhigh);
sensor_write(sd, 0x3502, explow);
sensor_write(sd, 0x3501, expmid);
sensor_write(sd, 0x3500, exphigh);
sensor_write(sd, 0x3208, 0x10);//end group write
sensor_write(sd, 0x3208, 0xa0);//init group write
info->exp = exp_val;
info->gain = gain_val;
return 0;
}
static void sensor_s_sw_stby(struct v4l2_subdev *sd, int on_off)
{
int ret = 0;
return ret;
}
/*
* Stuff that knows about the sensor.
*/
static int sensor_power(struct v4l2_subdev *sd, int on)
{
int ret = 0;
sensor_print("ov5648 sensor_power\n");
switch (on) {
case STBY_ON:
sensor_print("STBY_ON!\n");
cci_lock(sd);
sensor_s_sw_stby(sd, STBY_ON);
usleep_range(1000, 1200);
cci_unlock(sd);
break;
case STBY_OFF:
sensor_print("STBY_OFF!\n");
cci_lock(sd);
usleep_range(1000, 1200);
sensor_s_sw_stby(sd, STBY_OFF);
cci_unlock(sd);
break;
case PWR_ON:
sensor_print("PWR_ON!100\n");
cci_lock(sd);
vin_gpio_set_status(sd, PWDN, 1);
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
vin_gpio_set_status(sd, POWER_EN, 1);
vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
vin_gpio_write(sd, POWER_EN, CSI_GPIO_HIGH);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, IOVDD, ON);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, AVDD, ON);
vin_set_pmu_channel(sd, AFVDD, ON);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, DVDD, ON);
usleep_range(7000, 8000);
vin_set_mclk_freq(sd, MCLK);
vin_set_mclk(sd, ON);
usleep_range(10000, 12000);
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
vin_set_pmu_channel(sd, CAMERAVDD, ON);/*AFVCC ON*/
usleep_range(10000, 12000);
cci_unlock(sd);
break;
case PWR_OFF:
sensor_print("PWR_OFF!\n");
cci_lock(sd);
vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
vin_set_mclk(sd, OFF);
usleep_range(7000, 8000);
vin_set_pmu_channel(sd, DVDD, OFF);
vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
vin_gpio_write(sd, POWER_EN, CSI_GPIO_LOW);
vin_set_pmu_channel(sd, AVDD, OFF);
vin_set_pmu_channel(sd, IOVDD, OFF);
vin_set_pmu_channel(sd, AFVDD, OFF);
vin_set_pmu_channel(sd, CAMERAVDD, OFF);/*AFVCC ON*/
cci_unlock(sd);
break;
default:
return -EINVAL;
}
return 0;
}
static int sensor_reset(struct v4l2_subdev *sd, u32 val)
{
switch (val) {
case 0:
vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
usleep_range(10000,12000);
break;
case 1:
vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
usleep_range(10000,12000);
break;
default:
return -EINVAL;
}
return 0;
}
static int sensor_detect(struct v4l2_subdev *sd)
{
data_type rdval;
unsigned int SENSOR_ID = 0;
sensor_read(sd, 0x300A, &rdval);
SENSOR_ID |= rdval;
SENSOR_ID |= (rdval << 8);
sensor_read(sd, 0x300B, &rdval);
SENSOR_ID |= (rdval);
sensor_print("V4L2_IDENT_SENSOR = 0x%x\n", SENSOR_ID);
if (SENSOR_ID != V4L2_IDENT_SENSOR) {
sensor_print("ov5648 %s error, chip found is not an target chip", __func__);
//return -ENODEV;
}
return 0;
}
static int sensor_init(struct v4l2_subdev *sd, u32 val)
{
int ret;
struct sensor_info *info = to_state(sd);
sensor_print("sensor_init\n");
/*Make sure it is a target sensor */
ret = sensor_detect(sd);
if (ret) {
sensor_err("chip found is not an target chip.\n");
return ret;
}
info->focus_status = 0;
info->low_speed = 0;
info->width = QSXGA_WIDTH;
info->height = QSXGA_HEIGHT;
info->hflip = 0;
info->vflip = 0;
info->gain = 0;
info->tpf.numerator = 1;
info->tpf.denominator = 30; /* 30fps */
info->preview_first_flag = 1;
return 0;
}
static long sensor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
int ret = 0;
struct sensor_info *info = to_state(sd);
switch (cmd) {
case GET_CURRENT_WIN_CFG:
if (info->current_wins != NULL) {
memcpy(arg, info->current_wins,
sizeof(struct sensor_win_size));
ret = 0;
} else {
sensor_err("empty wins!\n");
ret = -1;
}
break;
case SET_FPS:
ret = 0;
break;
case VIDIOC_VIN_SENSOR_EXP_GAIN:
ret = sensor_s_exp_gain(sd, (struct sensor_exp_gain *)arg);
break;
case VIDIOC_VIN_SENSOR_CFG_REQ:
sensor_cfg_req(sd, (struct sensor_config *)arg);
break;
case VIDIOC_VIN_ACT_INIT:
ret = actuator_init(sd, (struct actuator_para *)arg);
break;
case VIDIOC_VIN_ACT_SET_CODE:
ret = actuator_set_code(sd, (struct actuator_ctrl *)arg);
break;
default:
return -EINVAL;
}
return ret;
}
/*
* Store information about the video data format.
*/
static struct sensor_format_struct sensor_formats[] = {
{
.desc = "Raw RGB Bayer",
.mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
.regs = sensor_fmt_raw,
.regs_size = ARRAY_SIZE(sensor_fmt_raw),
.bpp = 1
},
};
#define N_FMTS ARRAY_SIZE(sensor_formats)
/*
* Then there is the issue of window sizes. Try to capture the info here.
*/
static struct sensor_win_size sensor_win_sizes[] = {
{
.width = QSXGA_WIDTH,
.height = QSXGA_HEIGHT,
.hoffset = 0,
.voffset = 4,
.hts = 2752,
.vts = 1974,
.pclk = 81486720,
.mipi_bps = 408*1000*1000,
.fps_fixed = 2,
.bin_factor = 1,
.intg_min = 1,
.intg_max = (1974)<<4,
.gain_min = 1<<4,
.gain_max = 12<<4,
.regs = sensor_qsxga_regs,
.regs_size = ARRAY_SIZE(sensor_qsxga_regs),
.set_size = NULL,
},
{
.width = HD720_WIDTH,
.height = HD720_HEIGHT,
.hoffset = 0,
.voffset = 120,
.hts = 1896,
.vts = 984,
.pclk = 56*1000*1000,
.mipi_bps = 280*1000*1000,
.fps_fixed = 1,
.bin_factor = 1,
.intg_min = 1,
.intg_max = 984<<4,
.gain_min = 1<<4,
.gain_max = 12<<4,
.regs = sensor_720p_regs,//
.regs_size = ARRAY_SIZE(sensor_720p_regs),//
.set_size = NULL,
},
};
#define N_WIN_SIZES (ARRAY_SIZE(sensor_win_sizes))
static int sensor_reg_init(struct sensor_info *info)
{
int ret;
struct v4l2_subdev *sd = &info->sd;
struct sensor_format_struct *sensor_fmt = info->fmt;
struct sensor_win_size *wsize = info->current_wins;
ret = sensor_write_array(sd, sensor_default_regs,
ARRAY_SIZE(sensor_default_regs));
if (ret < 0) {
sensor_err("write sensor_default_regs error\n");
return ret;
}
sensor_print("sensor_reg_init\n");
sensor_write_array(sd, sensor_fmt->regs, sensor_fmt->regs_size);
if (wsize->regs)
sensor_write_array(sd, wsize->regs, wsize->regs_size);
if (wsize->set_size)
wsize->set_size(sd);
info->width = wsize->width;
info->height = wsize->height;
info->exp = 0;
info->gain = 0;
ov5648_sensor_vts = wsize->vts;
sensor_print("s_fmt set width = %d, height = %d\n", wsize->width,
wsize->height);
return 0;
}
static int sensor_s_stream(struct v4l2_subdev *sd, int enable)
{
struct sensor_info *info = to_state(sd);
sensor_print("%s on = %d, %d*%d fps: %d code: %x\n", __func__, enable,
info->current_wins->width, info->current_wins->height,
info->current_wins->fps_fixed, info->fmt->mbus_code);
if (!enable)
return 0;
return sensor_reg_init(info);
}
static int sensor_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
cfg->type = V4L2_MBUS_CSI2;
cfg->flags = 0 | V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0;
return 0;
}
static int sensor_g_ctrl(struct v4l2_ctrl *ctrl)
{
struct sensor_info *info = container_of(ctrl->handler,
struct sensor_info, handler);
struct v4l2_subdev *sd = &info->sd;
switch (ctrl->id) {
case V4L2_CID_GAIN:
return sensor_g_gain(sd, &ctrl->val);
case V4L2_CID_EXPOSURE:
return sensor_g_exp(sd, &ctrl->val);
}
return -EINVAL;
}
static int sensor_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct sensor_info *info = container_of(ctrl->handler,
struct sensor_info, handler);
struct v4l2_subdev *sd = &info->sd;
switch (ctrl->id) {
case V4L2_CID_GAIN:
return sensor_s_gain(sd, ctrl->val);
case V4L2_CID_EXPOSURE:
return sensor_s_exp(sd, ctrl->val);
}
return -EINVAL;
}
/* ----------------------------------------------------------------------- */
static const struct v4l2_ctrl_ops sensor_ctrl_ops = {
.g_volatile_ctrl = sensor_g_ctrl,
.s_ctrl = sensor_s_ctrl,
};
static const struct v4l2_subdev_core_ops sensor_core_ops = {
.reset = sensor_reset,
.init = sensor_init,
.s_power = sensor_power,
.ioctl = sensor_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = sensor_compat_ioctl32,
#endif
};
static const struct v4l2_subdev_video_ops sensor_video_ops = {
.s_parm = sensor_s_parm,
.g_parm = sensor_g_parm,
.s_stream = sensor_s_stream,
.g_mbus_config = sensor_g_mbus_config,
};
static const struct v4l2_subdev_pad_ops sensor_pad_ops = {
.enum_mbus_code = sensor_enum_mbus_code,
.enum_frame_size = sensor_enum_frame_size,
.get_fmt = sensor_get_fmt,
.set_fmt = sensor_set_fmt,
};
static const struct v4l2_subdev_ops sensor_ops = {
.core = &sensor_core_ops,
.video = &sensor_video_ops,
.pad = &sensor_pad_ops,
};
/* ----------------------------------------------------------------------- */
static struct cci_driver cci_drv = {
.name = SENSOR_NAME,
.addr_width = CCI_BITS_16,
.data_width = CCI_BITS_8,
};
static const struct v4l2_ctrl_config sensor_custom_ctrls[] = {
{
.ops = &sensor_ctrl_ops,
.id = V4L2_CID_FRAME_RATE,
.name = "frame rate",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 15,
.max = 120,
.step = 1,
.def = 120,
},
};
static int sensor_init_controls(struct v4l2_subdev *sd,
const struct v4l2_ctrl_ops *ops)
{
struct sensor_info *info = to_state(sd);
struct v4l2_ctrl_handler *handler = &info->handler;
struct v4l2_ctrl *ctrl;
int i;
int ret = 0;
v4l2_ctrl_handler_init(handler, 2 + ARRAY_SIZE(sensor_custom_ctrls));
v4l2_ctrl_new_std(handler, ops, V4L2_CID_GAIN, 1 * 1600,
256 * 1600, 1, 1 * 1600);
ctrl = v4l2_ctrl_new_std(handler, ops, V4L2_CID_EXPOSURE, 0,
65536 * 16, 1, 0);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
for (i = 0; i < ARRAY_SIZE(sensor_custom_ctrls); i++)
v4l2_ctrl_new_custom(handler, &sensor_custom_ctrls[i], NULL);
if (handler->error) {
ret = handler->error;
v4l2_ctrl_handler_free(handler);
}
sd->ctrl_handler = handler;
return ret;
}
static int sensor_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct v4l2_subdev *sd;
struct sensor_info *info;
info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
sd = &info->sd;
cci_dev_probe_helper(sd, client, &sensor_ops, &cci_drv);
sensor_init_controls(sd, &sensor_ctrl_ops);
mutex_init(&info->lock);
#ifdef CONFIG_SAME_I2C
info->sensor_i2c_addr = I2C_ADDR >> 1;
#endif
info->fmt = &sensor_formats[0];
info->fmt_pt = &sensor_formats[0];
info->win_pt = &sensor_win_sizes[0];
info->fmt_num = N_FMTS;
info->win_size_num = N_WIN_SIZES;
info->sensor_field = V4L2_FIELD_NONE;
info->stream_seq = MIPI_BEFORE_SENSOR;
info->af_first_flag = 1;
info->exp = 0;
info->gain = 0;
return 0;
}
static int sensor_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd;
sd = cci_dev_remove_helper(client, &cci_drv);
kfree(to_state(sd));
return 0;
}
static const struct i2c_device_id sensor_id[] = {
{SENSOR_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, sensor_id);
static struct i2c_driver sensor_driver = {
.driver = {
.owner = THIS_MODULE,
.name = SENSOR_NAME,
},
.probe = sensor_probe,
.remove = sensor_remove,
.id_table = sensor_id,
};
static __init int init_sensor(void)
{
return cci_dev_init_helper(&sensor_driver);
}
static __exit void exit_sensor(void)
{
cci_dev_exit_helper(&sensor_driver);
}
module_init(init_sensor);
module_exit(exit_sensor);
另外建议使用支持列表中的摄像头,例如gc2053,gc2063,这些摄像头已经适配量产完成并且调整ISP后画质更佳,也支持aiisp实现低照度全彩画质,树莓派的摄像头不推荐使用,因为他是外挂mclk的会引起芯片处于错误的模式,另外原厂也没有相应的支持(2017年前的芯片才有这个支持)
IMX219 同样可以使用,但是请注意4lane的摄像头不可适配2lane的数据
参考 G2D 驱动做下修改
lichee/linux-5.4/drivers/char/sunxi_g2d/g2d_rcq/g2d.c
void *g2d_malloc(__u32 bytes_num, __u32 *phy_addr)
{
void *address = NULL;
#if defined(CONFIG_ION)
u32 actual_bytes;
if (bytes_num != 0) {
actual_bytes = G2D_BYTE_ALIGN(bytes_num);
address = dma_alloc_coherent(para.dev, actual_bytes,
(dma_addr_t *) phy_addr,
GFP_KERNEL);
if (address) {
return address;
}
G2D_ERR_MSG("dma_alloc_coherent fail, size=0x%x\n", bytes_num);
return NULL;
}
G2D_ERR_MSG("size is zero\n");
#else
unsigned int map_size = 0;
struct page *page;
if (bytes_num != 0) {
map_size = PAGE_ALIGN(bytes_num);
page = alloc_pages(GFP_KERNEL, get_order(map_size));
if (page != NULL) {
address = page_address(page);
if (address == NULL) {
free_pages((unsigned long)(page),
get_order(map_size));
G2D_ERR_MSG("page_address fail!\n");
return NULL;
}
*phy_addr = virt_to_phys(address);
return address;
}
G2D_ERR_MSG("alloc_pages fail!\n");
return NULL;
}
G2D_ERR_MSG("size is zero\n");
#endif
return NULL;
}
目前看到 dma_alloc_coherent(NULL, xxx...) 的第一个参数是NULL,正常来说应该分配设备而不是NULL。
dclk过高,屏幕分屏了?
这里提供一个py脚本计算分频系数,这里对应的是HV屏,DSI也可以参考
def find_closest_clock(target_clock, clock_list):
clock_list = sorted(clock_list)
low, high = 0, len(clock_list) - 1
closest = clock_list[low]
while low <= high:
mid = (low + high) // 2
if clock_list[mid] < target_clock:
low = mid + 1
elif clock_list[mid] > target_clock:
high = mid - 1
else:
return clock_list[mid]
if abs(clock_list[mid] - target_clock) < abs(closest - target_clock):
closest = clock_list[mid]
return closest
def calculate_divisor(clock_need, clock_list, min_divisor=6):
is_perfect = True
for i in clock_list:
for j in range(0, 255):
if (clock_need * j) == i:
closest_clock = i
divisor = j
return closest_clock, divisor, is_perfect
is_perfect = False
closest_clock = find_closest_clock(clock_need, clock_list)
if closest_clock == 0:
return None, None, None
divisor = closest_clock // clock_need
if divisor < min_divisor:
min_diff = float('inf')
best_clock = None
for clock in clock_list:
if clock >= clock_need * min_divisor:
current_divisor = clock // clock_need
if current_divisor < min_divisor:
continue
diff = abs(clock - clock_need * current_divisor)
if diff < min_diff:
min_diff = diff
best_clock = clock
if best_clock is not None:
return best_clock, best_clock // clock_need, is_perfect
return closest_clock, divisor, is_perfect
clock_list = [
408, 420, 432, 444, 456, 468, 480, 492, 504, 516, 528, 540, 552, 564,
576, 588, 600, 612, 624, 636, 648, 660, 672, 684, 696, 708, 720, 732,
744, 756, 768, 780, 792, 804, 816, 828, 840, 852, 864, 876, 888, 900,
912, 924, 936, 948, 960, 972, 984, 996, 1008, 1020, 1032, 1044, 1056,
1068, 1080, 1092, 1104, 1116, 1128, 1140, 1152, 1164, 1176, 1188, 1200,
1212, 1224, 1236, 1248, 1260, 1272, 1284, 1296, 1308, 1320, 1332, 1344,
1356, 1368, 1380, 1392, 1404, 1416, 1428, 1440, 1452, 1464, 1476, 1488,
1500, 1512, 1524, 1536, 1548, 1560, 1572, 1584, 1596, 1608, 1620, 1632,
1644, 1656, 1668, 1680, 1692
]
clock_need = int(input("请输入需要的时钟(MHz): "))
closest_clock, divisor, is_perfect = calculate_divisor(clock_need, clock_list)
if is_perfect:
print(f"父时钟: {closest_clock}MHz, 分频系数: {divisor}, 分频后的频率: {closest_clock / divisor}MHz")
else:
print(f"无法找到完美,最近的父时钟: {closest_clock}, 分频系数: {divisor}, 分频后的频率: {closest_clock / divisor}MHz")
print("请修改分频系数表 clk_tbl 中 HV 分频系数为: {LCD_IF_HV, " + hex(divisor) + ", 1, 1, 0}")
会,启动介质优先级描述了每个介质被选择为启动介质的可能性。BROM 首先读取具有最高优先级的介质的 boot0。如果该介质不存在或存在任何问题,BROM 将尝试下一个介质。否则,该介质将被选择为启动介质。
具体可以查看手册GPIO Boot Select表格
根据具体的模型和需求的规格来看,实际感觉差不多