432 lines
11 KiB
C
432 lines
11 KiB
C
/*
|
|
* Copyright (c) 2022 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "ex_tc9591.h"
|
|
#include "camera/sdrv-cam-baremetal-def.h"
|
|
|
|
#define TC9591_DEVICE_ID 0x4401
|
|
#define TC9591_DESER_ADDR 0x0E
|
|
|
|
enum tc9591_status {
|
|
DESER_IDLE,
|
|
DESER_STREAM_ON,
|
|
};
|
|
|
|
struct tc9591_dev {
|
|
struct vdev_device vdev;
|
|
void *i2c_handle;
|
|
uint8_t deser_addr;
|
|
uint32_t gpio_rst;
|
|
uint32_t gpio_cam; //vcam 5v
|
|
uint32_t gpio_fsin;//for E3 Display 5019 Fsin pin
|
|
osMutexId_t lock;
|
|
uint32_t status;
|
|
};
|
|
|
|
|
|
static int tc9591_write_reg(struct tc9591_dev *deser, uint16_t reg, uint16_t val)
|
|
{
|
|
uint8_t buf[4];
|
|
int ret = 0;
|
|
|
|
deser_debug("%s: addr=0x%x, reg=0x%4x, val=0x%4x\n", __func__,
|
|
deser->deser_addr, reg, val);
|
|
|
|
buf[0] = reg >> 8;
|
|
buf[1] = reg & 0xff;
|
|
buf[2] = val >> 8;
|
|
buf[3] = val & 0xFF;
|
|
|
|
ret = i2c_write(deser->i2c_handle, deser->deser_addr, buf, 4);
|
|
if (ret < 0) {
|
|
printf("%s: error: reg=0x%04x, val=%04xx\n", __func__, reg, val);
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tc9591_read_reg(struct tc9591_dev *deser, uint16_t reg16, uint16_t *val)
|
|
{
|
|
uint8_t buf[2], reg[2];
|
|
int ret;
|
|
|
|
reg[0] = reg16 >> 8;
|
|
reg[1] = reg16 & 0xff;
|
|
memset(buf, 0, sizeof(buf));
|
|
ret = i2c_write_read(deser->i2c_handle, deser->deser_addr, reg, 2, buf, 2);
|
|
if (ret < 0) {
|
|
printf("%s: error: read reg=0x%04x\n", __func__, reg16);
|
|
return ret;
|
|
}
|
|
//printf("%s: OK: read reg=%x, val=%02x %02x\n", __func__, reg16, buf[0], buf[1]);
|
|
|
|
*val = (buf[0] << 8);
|
|
*val |= buf[1];
|
|
return 0;
|
|
}
|
|
|
|
static uint16_t tc9591_reg_list[] = {
|
|
0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000c, 0x000e,
|
|
0x0010, 0x0012, 0x0014, 0x0016, 0x0018, 0x0020, 0x0022, 0x0032,
|
|
0x0050, 0x0056, 0x0058, 0x005a, 0x005c, 0x005e, 0x0060, 0x0062,
|
|
0x0064, 0x0066, 0x0068, 0x006a, 0x006c, 0x006e, 0x0070, 0x0080,
|
|
0x0082, 0x0084, 0x0086, 0x0088, 0x008a, 0x008c, 0x008e, 0x0090,
|
|
0x00f8,
|
|
};
|
|
|
|
static int read_all_reg(struct tc9591_dev *deser)
|
|
{
|
|
int i, j;
|
|
uint16_t reg[8], val[8];
|
|
|
|
for (j = 0; j < 40; j+=8) {
|
|
for (i = 0; i < 8; i++) {
|
|
reg[i] = tc9591_reg_list[j+i];
|
|
tc9591_read_reg(deser, reg[i], &val[i]);
|
|
usleep_range(10000, 11000);
|
|
}
|
|
printf("tc9591 read [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x [0x%04x]=0x%04x\n",
|
|
reg[0], val[0], reg[1], val[1], reg[2], val[2], reg[3], val[3],
|
|
reg[4], val[4], reg[5], val[5], reg[6], val[6], reg[7], val[7]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_check_chip_id(struct tc9591_dev *deser)
|
|
{
|
|
int ret;
|
|
int i = 0;
|
|
uint16_t chip_id = 0;
|
|
|
|
printf("%s\n", __func__);
|
|
while (i++ < 3) {
|
|
ret = tc9591_read_reg(deser, 0x0000, &chip_id);
|
|
if (ret) {
|
|
printf("%s: failed to read chip identifier\n", __func__);
|
|
continue;
|
|
}
|
|
|
|
if (chip_id != TC9591_DEVICE_ID) {
|
|
printf("%s: wrong chip identifier, expected 0x%04x(tc9591), got "
|
|
"0x%04x\n",
|
|
__func__, TC9591_DEVICE_ID, chip_id);
|
|
ret = -1;
|
|
continue;
|
|
} else {
|
|
printf("%s: chip identifier, expected 0x%04x(tc9591), got 0x%04x\n",
|
|
__func__, TC9591_DEVICE_ID, chip_id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret == 0)
|
|
read_all_reg(deser);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tc9591_init_setup(struct tc9591_dev *deser)
|
|
{
|
|
uint16_t val;
|
|
int ret;
|
|
|
|
ret = tc9591_read_reg(deser, 0x0002, &val);
|
|
if (ret < 0) {
|
|
printf("tc9591 fail to read 0x02, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
printf("tc9591 read [0x0002]=0x%04x\n", val);
|
|
|
|
ret = tc9591_read_reg(deser, 0x0004, &val);
|
|
if (ret < 0) {
|
|
printf("tc9591 fail to read 0x04, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
printf("tc9591 read [0x0004]=0x%04x\n", val);
|
|
|
|
tc9591_write_reg(deser, 0x0002, 0x0001); // SYSctl, S/W Reset
|
|
mdelay(10);
|
|
tc9591_write_reg(deser, 0x0002, 0x0000); // SYSctl, S/W Reset release
|
|
|
|
tc9591_write_reg(deser, 0x0016, 0x205c); // PLL Control Register 0 (PLL_PRD,PLL_FBD)
|
|
|
|
tc9591_write_reg(deser, 0x0018, 0X0403); // PLL_FRS,PLL_LBWS, PLL oscillation enable
|
|
mdelay(10);
|
|
|
|
tc9591_write_reg(deser, 0x0018, 0X0413); // PLL_FRS,PLL_LBWS, PLL clock out enable
|
|
tc9591_write_reg(deser, 0x0020, 0x0011); // CLK control register: Clock divider setting
|
|
tc9591_write_reg(deser, 0x000c, 0x101); // MCLK duty setting
|
|
|
|
|
|
|
|
tc9591_write_reg(deser, 0x0010, 0xFFF9); // GPIO Direction, GPIO2,1 output
|
|
tc9591_write_reg(deser, 0x0014, 0x0000); // GPIO output data. GPIO2="L", GPIO1="L"
|
|
tc9591_write_reg(deser, 0x000E, 0x0006); // GPIO enable. GPIO2,1 enable
|
|
tc9591_write_reg(deser, 0x0014, 0x0006); // GPIO output data. GPIO2="H", GPIO1="H"
|
|
|
|
|
|
tc9591_write_reg(deser, 0x0060, 0x800f); //PHY timing delay setting
|
|
tc9591_write_reg(deser, 0x0006, 0x0032); // FIFO control
|
|
tc9591_write_reg(deser, 0x0008, 0x0061); // DATA FORMAT: yuv422-16bit
|
|
|
|
tc9591_write_reg(deser, 0x0066, 0x00FF); // enable all error
|
|
|
|
tc9591_write_reg(deser, 0x0004, 0x141);
|
|
|
|
|
|
printf("tc9591 init setup done\n");
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
//extern gpio_dev_t g_gpio_safety;
|
|
|
|
static int tc9591_s_power(struct vdev_device *vdev, int enable)
|
|
{
|
|
struct tc9591_dev *deser;
|
|
uint32_t pin;
|
|
if (!vdev) {
|
|
return -1;
|
|
}
|
|
|
|
printf("%s %d\r\n", __func__, enable);
|
|
deser = (struct tc9591_dev *)vdev->priv_data;
|
|
pin = deser->gpio_rst; /* GPIO_B1 */
|
|
|
|
if(enable) {
|
|
if (deser->status == DESER_STREAM_ON) {
|
|
printf("%s: already stream on\n", __func__);
|
|
goto exit;
|
|
}
|
|
printf("%s: pin %d, enable %d\n", __func__, pin, enable);
|
|
gpio_set(NULL, pin, false); // low level actice
|
|
usleep_range(1000, 1000);
|
|
gpio_set(NULL, pin, true);
|
|
} else {
|
|
gpio_set(NULL, pin, false);
|
|
}
|
|
exit:
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_enum_mbus_fmt(struct vdev_device *vdev,
|
|
struct vdev_mbus_framefmt *fme)
|
|
{
|
|
struct tc9591_dev *deser;
|
|
|
|
if ((vdev == NULL) || (fme == NULL)) {
|
|
return -1;
|
|
}
|
|
|
|
deser = (struct tc9591_dev *)vdev->priv_data;
|
|
|
|
if (fme->index > 0) {
|
|
fme->index = 0;
|
|
return -1;
|
|
}
|
|
|
|
fme->code = deser->vdev.fmt.code;
|
|
fme->field = deser->vdev.fmt.field;
|
|
fme->colorspace = deser->vdev.fmt.colorspace;
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_enum_frame_size(struct vdev_device *vdev,
|
|
struct vdev_frame_size_enum *fse)
|
|
{
|
|
struct tc9591_dev *deser;
|
|
|
|
if (!vdev || !fse) {
|
|
return -1;
|
|
}
|
|
|
|
deser = (struct tc9591_dev *)vdev->priv_data;
|
|
|
|
if (fse->index > 0) {
|
|
fse->index = 0;
|
|
return -1;
|
|
}
|
|
|
|
fse->min_width = deser->vdev.fmt.width;
|
|
fse->max_width = deser->vdev.fmt.width;
|
|
fse->min_height = deser->vdev.fmt.height;
|
|
fse->max_height = deser->vdev.fmt.height;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_enum_frame_interval(struct vdev_device *vdev,
|
|
struct vdev_frame_interval_enum *fie)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_get_frame_interval(struct vdev_device *vdev,
|
|
struct vdev_fract *fi)
|
|
{
|
|
if (!vdev || !fi) {
|
|
return -1;
|
|
}
|
|
|
|
*fi = vdev->frame_interval;
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_set_frame_interval(struct vdev_device *vdev,
|
|
struct vdev_fract fi)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_get_fmt(struct vdev_device *vdev,
|
|
struct vdev_mbus_framefmt *fmt)
|
|
{
|
|
if (!vdev) {
|
|
return -1;
|
|
}
|
|
|
|
*fmt = vdev->fmt;
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_set_fmt(struct vdev_device *vdev,
|
|
struct vdev_mbus_framefmt fmt)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int tc9591_s_stream(struct vdev_device *vdev, int enable)
|
|
{
|
|
int ret = 0;
|
|
uint32_t cam_pwr = 0;
|
|
|
|
struct tc9591_dev *deser;
|
|
|
|
if (!vdev) {
|
|
return -1;
|
|
}
|
|
printf("%s %d\r\n", __func__, enable);
|
|
deser = (struct tc9591_dev *)vdev->priv_data;
|
|
osMutexAcquire(deser->lock, 0);
|
|
cam_pwr = deser->gpio_cam;
|
|
|
|
if(cam_pwr <=0 ) {
|
|
sensor_info("%s get cam pwr pin err\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
if (enable == 1) {
|
|
|
|
if (deser->status == DESER_STREAM_ON) {
|
|
printf("%s: already stream on\n", __func__);
|
|
goto exit;
|
|
}
|
|
gpio_set(NULL, cam_pwr, false);
|
|
ret = tc9591_init_setup(deser);
|
|
if (ret < 0)
|
|
goto exit;
|
|
|
|
usleep_range(10000,11000);
|
|
gpio_set(NULL, cam_pwr, true);
|
|
deser->status = DESER_STREAM_ON;
|
|
|
|
} else {
|
|
deser->status = DESER_IDLE;
|
|
tc9591_write_reg(deser, 0x0004, 0x0147);
|
|
}
|
|
read_all_reg(deser);
|
|
exit:
|
|
osMutexRelease(deser->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int tc9591_deinit(struct vdev_device *vdev)
|
|
{
|
|
struct tc9591_dev *deser;
|
|
|
|
if (!vdev) {
|
|
return -1;
|
|
}
|
|
|
|
|
|
deser = (struct tc9591_dev *)vdev->priv_data;
|
|
osMutexDelete(deser->lock);
|
|
free(deser);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct vdev_dev_ops tc9591_vdev_ops = {
|
|
.s_power = tc9591_s_power,
|
|
.enum_mbus_fmt = tc9591_enum_mbus_fmt,
|
|
.enum_frame_size = tc9591_enum_frame_size,
|
|
.enum_frame_interval = tc9591_enum_frame_interval,
|
|
.g_frame_interval = tc9591_get_frame_interval,
|
|
.s_frame_interval = tc9591_set_frame_interval,
|
|
.get_fmt = tc9591_get_fmt,
|
|
.set_fmt = tc9591_set_fmt,
|
|
.s_stream = tc9591_s_stream,
|
|
.close = tc9591_deinit,
|
|
};
|
|
|
|
struct tc9591_dev g_tc9591;
|
|
|
|
struct vdev_device *tc9591_init(void *init_data)
|
|
{
|
|
int ret;
|
|
struct tc9591_dev *deser;
|
|
struct camera_res_cfg *cfg = (struct camera_res_cfg *)init_data;
|
|
|
|
//deser = malloc(sizeof(struct tc9591_dev));
|
|
deser = &g_tc9591;
|
|
if (!deser || !cfg || !cfg->i2c_handle)
|
|
return NULL;
|
|
|
|
printf("%s \r\n", __func__);
|
|
memset(deser, 0, sizeof(struct tc9591_dev));
|
|
deser->i2c_handle = cfg->i2c_handle;
|
|
deser->deser_addr = TC9591_DESER_ADDR;
|
|
|
|
deser->vdev.priv_data = (void *)deser;
|
|
deser->lock = osMutexNew(NULL);
|
|
deser->gpio_rst = cfg->gpio[0]; //rst pin
|
|
deser->gpio_cam = cfg->gpio[1]; //vcam
|
|
deser->gpio_fsin = cfg->gpio[2]; //fsin
|
|
|
|
tc9591_s_power(&deser->vdev, 1); //power on
|
|
ret = tc9591_check_chip_id(deser);
|
|
if (ret == 0)
|
|
tc9591_s_stream(&deser->vdev, 1);
|
|
|
|
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
deser->vdev.ep.bus_type = VDEV_MBUS_PARALLEL2;
|
|
deser->vdev.frame_interval.numerator = 1;
|
|
deser->vdev.frame_interval.denominator = 60;
|
|
deser->vdev.fmt.width = 1920;
|
|
deser->vdev.fmt.height = 720;
|
|
deser->vdev.fmt.code = VDEV_MBUS_FMT_UYVY16;
|
|
deser->vdev.fmt.field = VDEV_FIELD_NONE;
|
|
deser->vdev.fmt.colorspace = VDEV_COLORSPACE_SRGB;
|
|
deser->vdev.ops = tc9591_vdev_ops;
|
|
|
|
return &deser->vdev;
|
|
|
|
err:
|
|
free(deser);
|
|
return NULL;
|
|
}
|
|
|
|
void read_tc9591_regs(void)
|
|
{
|
|
read_all_reg(&g_tc9591);
|
|
}
|
|
|