/* * Copyright (c) 2024-2025, ArtInChip Technology Co., Ltd * * SPDX-License-Identifier: Apache-2.0 * * Authors: matteo */ #define LOG_TAG "sc03" #include #include #include "aic_core.h" #include "mpp_types.h" #include "mpp_img_size.h" #include "mpp_vin.h" #include "drv_camera.h" #include "camera_inner.h" #ifdef AIC_USING_CAMERA_SC030IOT #define DEV_NAME "SC030IOT" #endif #ifdef AIC_USING_CAMERA_SC031IOT #define DEV_NAME "SC031IOT" #endif /* Default format configuration of SC03XIOT */ #define SC03XIOT_DFT_WIDTH VGA_WIDTH #define SC03XIOT_DFT_HEIGHT VGA_HEIGHT #define SC03XIOT_DFT_BUS_TYPE MEDIA_BUS_PARALLEL #define SC03XIOT_DFT_CODE MEDIA_BUS_FMT_YUYV8_2X8 #define SC03XIOT_I2C_SLAVE_ID 0x68 #define SC03XIOT_CHIP_ID 0x9A46 #define SC03_SENSOR_ID_HIGH_REG 0xF7 #define SC03_SENSOR_ID_LOW_REG 0xF8 // 640*480, xclk=20M, fps=50fps, xclk=10M, fps=25fps static const struct reg8_info sc03xiot_init_regs[] = { {0xf0, 0x30}, {0x01, 0xff}, {0x02, 0xff}, {0x22, 0x07}, {0x19, 0xff}, {0x3f, 0x82}, {0x30, 0x02}, {0xf0, 0x01}, {0x70, 0x00}, {0x71, 0x80}, {0x72, 0x20}, {0x73, 0x00}, {0x74, 0xe0}, {0x75, 0x10}, {0x76, 0x81}, {0x77, 0x88}, {0x78, 0xe1}, #ifdef AIC_USING_CAMERA_SC030IOT {0x79, 0x01}, #endif #ifdef AIC_USING_CAMERA_SC031IOT {0x79, 0x31}, #endif {0xf5, 0x01}, {0xf4, 0x0a}, {0xf0, 0x36}, {0x37, 0x79}, #ifdef AIC_USING_CAMERA_SC031IOT {0xea, 0x09}, #endif {0x31, 0x82}, {0x3e, 0x60}, {0x30, 0xf0}, {0x33, 0x33}, {0xf0, 0x32}, {0x48, 0x02}, {0xf0, 0x33}, {0x02, 0x12}, {0x7c, 0x02}, {0x7d, 0x0e}, {0xa2, 0x04}, {0x5e, 0x06}, {0x5f, 0x0a}, {0x0b, 0x58}, {0x06, 0x38}, {0xf0, 0x32}, {0x48, 0x02}, {0xf0, 0x39}, {0x02, 0x70}, {0xf0, 0x45}, {0x09, 0x1c}, {0xf0, 0x37}, {0x22, 0x0d}, {0xf0, 0x33}, {0x33, 0x10}, {0xb1, 0x80}, {0x34, 0x40}, {0x0b, 0x54}, {0xb2, 0x78}, {0xf0, 0x36}, {0x11, 0x80}, {0xf0, 0x30}, {0x38, 0x44}, {0xf0, 0x33}, {0xb3, 0x51}, {0x01, 0x10}, {0x0b, 0x6c}, {0x06, 0x24}, {0xf0, 0x36}, {0x31, 0x82}, {0x3e, 0x60}, {0x30, 0xf0}, {0x33, 0x33}, {0xf0, 0x34}, {0x9f, 0x02}, {0xa6, 0x40}, {0xa7, 0x47}, {0xe8, 0x5f}, {0xa8, 0x51}, {0xa9, 0x44}, {0xe9, 0x36}, {0xf0, 0x33}, {0xb3, 0x51}, {0x64, 0x17}, {0x90, 0x01}, {0x91, 0x03}, {0x92, 0x07}, {0x01, 0x10}, {0x93, 0x10}, {0x94, 0x10}, {0x95, 0x10}, {0x96, 0x01}, {0x97, 0x07}, {0x98, 0x1f}, {0x99, 0x10}, {0x9a, 0x20}, {0x9b, 0x28}, {0x9c, 0x28}, {0xf0, 0x36}, {0x70, 0x54}, {0xb6, 0x40}, {0xb7, 0x41}, {0xb8, 0x43}, {0xb9, 0x47}, {0xba, 0x4f}, {0xb0, 0x8b}, {0xb1, 0x8b}, {0xb2, 0x8b}, {0xb3, 0x9b}, {0xb4, 0xb8}, {0xb5, 0xf0}, {0x7e, 0x41}, {0x7f, 0x47}, {0x77, 0x80}, {0x78, 0x84}, {0x79, 0x8a}, {0xa0, 0x47}, {0xa1, 0x5f}, {0x96, 0x43}, {0x97, 0x44}, {0x98, 0x54}, {0xf0, 0x00}, {0xf0, 0x01}, {0x73, 0x00}, {0x74, 0xe0}, {0x70, 0x00}, {0x71, 0x80}, {0xf0, 0x36}, {0x37, 0x74}, {0xf0, 0x3f}, #ifdef AIC_USING_CAMERA_SC030IOT {0x03, 0xa1}, #endif #ifdef AIC_USING_CAMERA_SC031IOT {0x03, 0x90}, #endif {0xf0, 0x36},//cvbs_off {0x11, 0x80}, {0xf0, 0x01}, {0x79, 0xc1}, {0xf0, 0x37}, {0x24, 0x21}, {0xf0, 0x36}, #ifdef AIC_USING_CAMERA_SC030IOT {0x41, 0x00}, {0xea, 0x09}, {0xeb, 0x03}, {0xec, 0x19}, {0xed, 0x38}, {0xe9, 0x30}, {0xf0, 0x33}, {0x33, 0x00}, {0x34, 0x00}, {0xb1, 0x00}, {0xf0, 0x00}, {0xe0, 0x04}, {0xf0, 0x01}, {0x73, 0x00}, {0x74, 0xe0}, {0x70, 0x00}, {0x71, 0x80}, {0xf0, 0x36}, {0x32, 0x44}, {0xf0, 0x36}, {0x3e, 0xe0}, {0x70, 0x56}, {0x7c, 0x43}, {0x7d, 0x47}, {0x74, 0x00}, {0x75, 0x00}, {0x76, 0x00}, {0xa0, 0x47}, {0xa1, 0x5f}, {0x96, 0x22}, {0x97, 0x22}, {0x98, 0x22}, #endif #ifdef AIC_USING_CAMERA_SC031IOT {0x41, 0x61}, #endif {0xf0, 0x00}, {0x72, 0x38}, {0x7a, 0x80}, {0x85, 0x18}, {0x9b, 0x35}, {0x9e, 0x20}, {0xd0, 0x66}, {0xd1, 0x34}, {0Xd3, 0x44}, {0xd6, 0x44}, {0xb0, 0x41}, {0xb2, 0x48}, {0xb3, 0xf4}, {0xb4, 0x0b}, {0xb5, 0x78}, {0xba, 0xff}, {0xbb, 0xc0}, {0xbc, 0x90}, {0xbd, 0x3a}, {0xc1, 0x67}, {0xf0, 0x01}, {0x20, 0x11}, {0x23, 0x90}, {0x24, 0x15}, {0x25, 0x87}, {0xbc, 0x9f}, {0xbd, 0x3a}, {0x48, 0xe6}, {0x49, 0xc0}, {0x4a, 0xd0}, {0x4b, 0x48}, }; struct sc03_dev { struct rt_device dev; struct rt_i2c_bus_device *i2c; u32 pwdn_pin; struct clk *clk; struct mpp_video_fmt fmt; bool on; bool streaming; }; static struct sc03_dev g_sc03_dev = {0}; static int sc03_write_reg(struct rt_i2c_bus_device *i2c, u8 reg, u8 val) { if (rt_i2c_write_reg(i2c, SC03XIOT_I2C_SLAVE_ID, reg, &val, 1) != 1) { LOG_E("%s: error: reg = 0x%x, val = 0x%x", __func__, reg, val); return -1; } return 0; } static int sc03_read_reg(struct rt_i2c_bus_device *i2c, u8 reg, u8 *val) { if (rt_i2c_read_reg(i2c, SC03XIOT_I2C_SLAVE_ID, reg, val, 1) != 1) { LOG_E("%s: error: reg = 0x%x, val = 0x%x", __func__, reg, *val); return -1; } return 0; } static int sc03_init(struct sc03_dev *sensor) { int i = 0; const struct reg8_info *info = sc03xiot_init_regs; for (i = 0; i < ARRAY_SIZE(sc03xiot_init_regs); i++, info++) { if (sc03_write_reg(sensor->i2c, info->reg, info->val)) return -1; } return 0; } static int sc03_probe(struct sc03_dev *sensor) { u8 id_h = 0, id_l = 0; if (sc03_read_reg(sensor->i2c, SC03_SENSOR_ID_LOW_REG, &id_l) || sc03_read_reg(sensor->i2c, SC03_SENSOR_ID_HIGH_REG, &id_h)) return -1; if ((id_h << 8 | id_l) != SC03XIOT_CHIP_ID) { LOG_E("Invalid chip ID: %02x %02x\n", id_h, id_l); return -1; } return sc03_init(sensor); } static bool sc03_is_open(struct sc03_dev *sensor) { return sensor->on; } static void sc03_power_on(struct sc03_dev *sensor) { if (sensor->on) return; camera_pin_set_high(sensor->pwdn_pin); aicos_udelay(2); LOG_I("Power on"); sensor->on = true; } static void sc03_power_off(struct sc03_dev *sensor) { if (!sensor->on) return; camera_pin_set_low(sensor->pwdn_pin); LOG_I("Power off"); sensor->on = false; } static rt_err_t sc03_ops_init(rt_device_t dev) { struct sc03_dev *sensor = (struct sc03_dev *)dev; sensor->i2c = camera_i2c_get(); if (!sensor->i2c) return -RT_EINVAL; sensor->fmt.code = SC03XIOT_DFT_CODE; sensor->fmt.width = SC03XIOT_DFT_WIDTH; sensor->fmt.height = SC03XIOT_DFT_HEIGHT; sensor->fmt.bus_type = SC03XIOT_DFT_BUS_TYPE; sensor->fmt.flags = MEDIA_SIGNAL_HSYNC_ACTIVE_HIGH | MEDIA_SIGNAL_VSYNC_ACTIVE_LOW | MEDIA_SIGNAL_PCLK_SAMPLE_FALLING; sensor->pwdn_pin = camera_pwdn_pin_get(); if (!sensor->pwdn_pin) return -RT_EINVAL; return RT_EOK; } static rt_err_t sc03_ops_open(rt_device_t dev, rt_uint16_t oflag) { struct sc03_dev *sensor = (struct sc03_dev *)dev; if (sc03_is_open(sensor)) return RT_EOK; sc03_power_on(sensor); if (sc03_probe(sensor)) { sc03_power_off(sensor); return -RT_ERROR; } LOG_I("%s inited", DEV_NAME); return RT_EOK; } static rt_err_t sc03_ops_close(rt_device_t dev) { struct sc03_dev *sensor = (struct sc03_dev *)dev; if (!sc03_is_open(sensor)) return -RT_ERROR; sc03_power_off(sensor); return RT_EOK; } static int sc03_get_fmt(rt_device_t dev, struct mpp_video_fmt *cfg) { struct sc03_dev *sensor = (struct sc03_dev *)dev; cfg->code = sensor->fmt.code; cfg->width = sensor->fmt.width; cfg->height = sensor->fmt.height; cfg->flags = sensor->fmt.flags; cfg->bus_type = sensor->fmt.bus_type; return RT_EOK; } static int sc03_start(rt_device_t dev) { return 0; } static int sc03_stop(rt_device_t dev) { return 0; } static rt_err_t sc03_ops_control(rt_device_t dev, int cmd, void *args) { switch (cmd) { case CAMERA_CMD_START: return sc03_start(dev); case CAMERA_CMD_STOP: return sc03_stop(dev); case CAMERA_CMD_GET_FMT: return sc03_get_fmt(dev, (struct mpp_video_fmt *)args); default: LOG_I("Unsupported cmd: 0x%x", cmd); return -RT_EINVAL; } return RT_EOK; } #ifdef RT_USING_DEVICE_OPS static const struct rt_device_ops sc03_ops = { .init = sc03_ops_init, .open = sc03_ops_open, .close = sc03_ops_close, .control = sc03_ops_control, }; #endif int rt_hw_sc03_init(void) { #ifdef RT_USING_DEVICE_OPS g_sc03_dev.dev.ops = &sc03_ops; #else g_sc03_dev.dev.init = sc03_ops_init; g_sc03_dev.dev.open = sc03_ops_open; g_sc03_dev.dev.close = sc03_ops_close; g_sc03_dev.dev.control = sc03_ops_control; #endif g_sc03_dev.dev.type = RT_Device_Class_CAMERA; rt_device_register(&g_sc03_dev.dev, CAMERA_DEV_NAME, 0); return 0; } INIT_DEVICE_EXPORT(rt_hw_sc03_init);