diff --git a/CODEOWNERS b/CODEOWNERS index 145d90e4b718..b7646e74dc2e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -171,6 +171,7 @@ /drivers/*/*xec* @franciscomunoz @albertofloyd @scottwcpg /drivers/wifi/ @jukkar @tbursztyka @pfalcon /drivers/wifi/eswifi/ @loicpoulain +/drivers/camera/ @JunYangNXP /dts/arm/atmel/samr21.dtsi @benpicco /dts/arm/st/ @erwango /dts/arm/ti/cc13?2* @bwitherspoon @@ -302,6 +303,7 @@ /samples/subsys/shell/ @jakub-uC @nordic-krch /samples/subsys/usb/ @jfischer-phytec-iot @finikorg /samples/subsys/power/ @wentongwu @pizi-nordic +/samples/camera/ @JunYangNXP /scripts/coccicheck @himanshujha199640 @JuliaLawall /scripts/coccinelle/ @himanshujha199640 @JuliaLawall /scripts/kconfig/ @ulfalizer diff --git a/boards/arm/mimxrt1050_evk/Kconfig.defconfig b/boards/arm/mimxrt1050_evk/Kconfig.defconfig index d028f9fa4a6a..c970e8920838 100644 --- a/boards/arm/mimxrt1050_evk/Kconfig.defconfig +++ b/boards/arm/mimxrt1050_evk/Kconfig.defconfig @@ -97,4 +97,11 @@ endchoice endif # LVGL +if CAMERA + +config CAMERA_DISPLAY_DEV_NAME + default "ELCDIF_1" + +endif + endif # BOARD_MIMXRT1050_EVK || BOARD_MIMXRT1050_EVK_QSPI diff --git a/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts b/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts index 68b47eb462c7..b4b47665b4ae 100644 --- a/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts +++ b/boards/arm/mimxrt1050_evk/mimxrt1050_evk.dts @@ -101,6 +101,16 @@ arduino_serial: &uart3 {}; int1-gpios = <&gpio1 10 0>; int2-gpios = <&gpio1 11 0>; }; + + /* I2C address of image sensor is not fixed. + * Camera driver scans the image sensor support list to + * identify the image sensor present. + */ + image-senosr@3ff { + compatible = "zephyr,image-sensor"; + reg = <0x3ff>; + pwr-gpios = <&gpio1 4 1>; + }; }; &uart1 { @@ -128,3 +138,7 @@ arduino_serial: &uart3 {}; pwr-gpios = <&gpio1 5 0>; cd-gpios = <&gpio2 28 0>; }; + +&csi1 { + status = "okay"; +}; diff --git a/boards/arm/mimxrt1050_evk/pinmux.c b/boards/arm/mimxrt1050_evk/pinmux.c index 44d2bb44d27b..5bc628bffcc9 100644 --- a/boards/arm/mimxrt1050_evk/pinmux.c +++ b/boards/arm/mimxrt1050_evk/pinmux.c @@ -102,6 +102,43 @@ static void mimxrt1050_evk_usdhc_pinmux( } #endif +#ifdef CONFIG_CAMERA +static void mimxrt1050_evk_csi_mclk_enable(bool enable) +{ + if (enable) { + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_05_CSI_MCLK, 0U); + } else { + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_05_GPIO1_IO21, 0U); + } +} + +static void mimxrt1050_evk_csi_input_cfg(void) +{ + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_04_CSI_PIXCLK, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_06_CSI_VSYNC, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_07_CSI_HSYNC, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_08_CSI_DATA09, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_09_CSI_DATA08, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_10_CSI_DATA07, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_11_CSI_DATA06, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_12_CSI_DATA05, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_13_CSI_DATA04, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_14_CSI_DATA03, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_15_CSI_DATA02, 0U); +} + +static void mimxrt1050_evk_camera_init(void) +{ + mimxrt1050_evk_csi_mclk_enable(false); + mimxrt1050_evk_csi_input_cfg(); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_04_GPIO1_IO04, 0U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL, 1U); + IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA, 1U); + IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_00_LPI2C1_SCL, 0xD8B0u); + IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B1_01_LPI2C1_SDA, 0xD8B0u); +} +#endif + static int mimxrt1050_evk_init(struct device *dev) { ARG_UNUSED(dev); @@ -304,6 +341,11 @@ static int mimxrt1050_evk_init(struct device *dev) imxrt_usdhc_pinmux_cb_register(mimxrt1050_evk_usdhc_pinmux); #endif +#ifdef CONFIG_CAMERA + mimxrt1050_evk_camera_init(); + imxrt_csi_mclk_cb_register(mimxrt1050_evk_csi_mclk_enable); +#endif + return 0; } diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 57acca6826c1..24ff216f8590 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -33,6 +33,7 @@ add_subdirectory_if_kconfig(wifi) add_subdirectory_if_kconfig(can) add_subdirectory_if_kconfig(audio) add_subdirectory_if_kconfig(hwinfo) +add_subdirectory_if_kconfig(camera) add_subdirectory_ifdef(CONFIG_FLASH_HAS_DRIVER_ENABLED flash) add_subdirectory_ifdef(CONFIG_SERIAL_HAS_DRIVER serial) diff --git a/drivers/Kconfig b/drivers/Kconfig index 662c02f7baa2..921ed184526d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -89,4 +89,6 @@ source "drivers/neural_net/Kconfig" source "drivers/hwinfo/Kconfig" +source "drivers/camera/Kconfig" + endmenu diff --git a/drivers/camera/CMakeLists.txt b/drivers/camera/CMakeLists.txt new file mode 100644 index 000000000000..94c5f01aacbf --- /dev/null +++ b/drivers/camera/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +add_subdirectory_ifdef(CONFIG_MT9M114 mt9m114) +add_subdirectory_ifdef(CONFIG_NXP_MCUX_CSI nxp_csi) + +zephyr_sources(camera_dev.c) +zephyr_sources(image_sensor_dev.c) + +zephyr_sources_ifdef(CONFIG_USERSPACE camera_handlers.c) +zephyr_sources_ifdef(CONFIG_USERSPACE image_sensor_handlers.c) diff --git a/drivers/camera/Kconfig b/drivers/camera/Kconfig new file mode 100644 index 000000000000..1dc7cf1b1d4f --- /dev/null +++ b/drivers/camera/Kconfig @@ -0,0 +1,64 @@ +# Kconfig - Camera drivers + +# +# Copyright (c) 2019 NXP Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig CAMERA + bool "Camera Drivers" + help + Enable camera drivers + +if CAMERA + +module = CAMERA +module-str = camera + +config CAMERA_DISPLAY_DEV_NAME + string "Camera display device name" + default "CAMERA DISPLAY" + help + Name of the display device to display camera. + +config IMAGE_SENSOR_INIT_PRIO + int "Image sensor initalization priority level" + default 70 + range 70 80 + help + Image sensor initialization priority level. + This number tells how early in the boot + the image sensor driver and image sensor vendor + driver are initialized. + +config CAMERA_INIT_PRIO + int "Camera initalization priority level" + default 81 + range 81 90 + help + Camera initialization priority level. + This number tells how early in the boot + the camera vendor drivers are initialized. + Camera initalization depneds on image sensors, + so CAMERA_INIT_PRIO must be greater than + IMAGE_SENSOR_INIT_PRIO. + +menuconfig NXP_MCUX_CSI + bool "NXP i.MXRT cmos sensor interface" + depends on HAS_MCUX_CSI + help + Enable NXP i.MXRT cmos sensor driver. + +source "drivers/camera/nxp_csi/Kconfig" + +menuconfig MT9M114 + bool "MT9M114 image sensor" + depends on I2C + default y + help + Enable driver for the MT9M114 image sensor. + +source "drivers/camera/mt9m114/Kconfig" + +endif diff --git a/drivers/camera/camera_dev.c b/drivers/camera/camera_dev.c new file mode 100644 index 000000000000..40896ffb0218 --- /dev/null +++ b/drivers/camera/camera_dev.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2019 NXP Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#define LOG_LEVEL LOG_LEVEL_ERR +#include +#define CAMERA_DEV_DBG + +#undef LOG_ERR +#define LOG_ERR printk + +#ifdef CAMERA_DEV_DBG +#undef LOG_INF +#define LOG_INF printk +#endif + +static int z_camera_num; +static struct device *z_camera_dev[CAMERA_MAX_NUMBER]; + +#define CAMERA_DRV_DATA_MAX_SIZE 1024 +static u8_t z_camera_drv_data[CAMERA_MAX_NUMBER * + CAMERA_DRV_DATA_MAX_SIZE] __aligned(64); + +static K_MUTEX_DEFINE(z_camera_lock); + +int camera_dev_get_cap(struct device *cam_dev, + struct camera_capability *cap) +{ + struct camera_driver_data *data = cam_dev->driver_data; + + memcpy((char *)cap, (char *)&data->cap, + sizeof(struct camera_capability)); + + return 0; +} + +int camera_dev_configure(struct device *cam_dev, + struct camera_fb_cfg *fb_cfg) +{ + struct camera_driver_data *data = cam_dev->driver_data; + + if (fb_cfg->cfg_mode == CAMERA_DEFAULT_CFG) { + memcpy((char *)&fb_cfg->fb_attr, + (char *)&data->fb_attr, + sizeof(struct camera_fb_attr)); + } else { + memcpy((char *)&data->fb_attr, + (char *)&fb_cfg->fb_attr, + sizeof(struct camera_fb_attr)); + } + + return 0; +} + +int camera_dev_register(struct device *dev) +{ + (void)k_mutex_lock(&z_camera_lock, K_FOREVER); + + if (z_camera_num < CAMERA_MAX_NUMBER) { + z_camera_dev[z_camera_num] = dev; + z_camera_num++; + + k_mutex_unlock(&z_camera_lock); + + return 0; + } + + k_mutex_unlock(&z_camera_lock); + + return -ENOSPC; +} + +struct camera_driver_data *camera_drv_data_alloc( + u32_t priv_size, enum camera_id id, bool clear) +{ + struct camera_driver_data *data; + + if ((priv_size + sizeof(struct camera_driver_data)) > + CAMERA_DRV_DATA_MAX_SIZE) { + + LOG_ERR("Camera data alloc size %d exceeds max size\r\n", + (int)(CAMERA_DRV_DATA_MAX_SIZE - + sizeof(struct camera_driver_data))); + return 0; + } + + if (id != CAMERA_PRIMARY_ID && + id != CAMERA_SECONDARY_ID) { + + LOG_ERR("Camera data alloc id %d is illegal\r\n", id); + return 0; + } + + data = (struct camera_driver_data *) + &z_camera_drv_data[(id - CAMERA_PRIMARY_ID) * + CAMERA_DRV_DATA_MAX_SIZE]; + if (clear) { + memset((char *)data, 0, sizeof(struct camera_driver_data)); + } + data->id = id; + + return data; +} + +static struct device *camera_get_by_id(enum camera_id id) +{ + int i; + struct device *dev; + struct camera_driver_data *camera_data; + + (void)k_mutex_lock(&z_camera_lock, K_FOREVER); + + for (i = 0; i < z_camera_num; i++) { + dev = z_camera_dev[i]; + camera_data = dev->driver_data; + if (camera_data->id == id) { + + k_mutex_unlock(&z_camera_lock); + + return dev; + } + } + + k_mutex_unlock(&z_camera_lock); + + return 0; +} + +struct device *camera_get_primary(void) +{ + return camera_get_by_id(CAMERA_PRIMARY_ID); +} + +struct device *camera_get_secondary(void) +{ + return camera_get_by_id(CAMERA_SECONDARY_ID); +} diff --git a/drivers/camera/camera_handlers.c b/drivers/camera/camera_handlers.c new file mode 100644 index 000000000000..74609d9db577 --- /dev/null +++ b/drivers/camera/camera_handlers.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 NXP Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +Z_SYSCALL_HANDLER(camera_power, cam_dev, power) +{ + return z_impl_camera_power((struct device *)cam_dev, + (bool)power); +} + +Z_SYSCALL_HANDLER(camera_get_cap, cam_dev, cap) +{ + return z_impl_camera_get_cap((struct device *)cam_dev, + (struct camera_capability *)cap); +} + +Z_SYSCALL_HANDLER(camera_reset, cam_dev) +{ + return z_impl_camera_reset((struct device *)cam_dev); +} + +Z_SYSCALL_HANDLER(camera_configure, cam_dev, + width, height, pixformat) +{ + return z_impl_camera_configure((struct device *)cam_dev, + (u16_t)width, (u16_t)height, + (enum display_pixel_format)pixformat); +} + +Z_SYSCALL_HANDLER(camera_start, cam_dev, + mode, bufs, buf_num, cb) +{ + return z_impl_camera_start((struct device *)cam_dev, + (enum camera_mode)mode, (void **)bufs, (u8_t)buf_num, + (camera_capture_cb)cb); +} + +Z_SYSCALL_HANDLER(camera_acquire_fb, cam_dev, + fb, timeout) +{ + return z_impl_camera_acquire_fb((struct device *)cam_dev, + (void **)fb, (s32_t)timeout); +} + +Z_SYSCALL_HANDLER(camera_release_fb, cam_dev, + fb) +{ + return z_impl_camera_release_fb((struct device *)cam_dev, + (void *)fb); +} diff --git a/drivers/camera/image_sensor_dev.c b/drivers/camera/image_sensor_dev.c new file mode 100644 index 000000000000..4fc912be15cd --- /dev/null +++ b/drivers/camera/image_sensor_dev.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2019 NXP Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#define LOG_LEVEL LOG_LEVEL_ERR +#include +LOG_MODULE_REGISTER(IMAGE_SENSOR_DEV); + +#undef LOG_ERR +#define LOG_ERR printk + +#undef LOG_INF +#define LOG_INF printk + +/*Contain all the CMOS sensors + * supported in current Zephyr. + */ +static bool z_img_sensor_support_list_init; +static sys_dlist_t z_img_sensor_support_list; + +static K_MUTEX_DEFINE(z_img_sensor_support_lock); + +void img_sensor_support_add(struct img_sensor_info *img_sensor) +{ + (void)k_mutex_lock(&z_img_sensor_support_lock, K_FOREVER); + + if (!z_img_sensor_support_list_init) { + z_img_sensor_support_list_init = true; + sys_dlist_init(&z_img_sensor_support_list); + } + + sys_dlist_append(&z_img_sensor_support_list, &img_sensor->node); + + LOG_INF("Add image sensor (id %d) into support list\r\n", + img_sensor->sensor_client.sensor_id); + + k_mutex_unlock(&z_img_sensor_support_lock); +} + +/*Image sensors present on the device. + * Image sensors devices are populated + * in board DTS. + */ +static int z_img_sensor_num; +static struct device *z_img_sensor_dev[CAMERA_MAX_NUMBER]; + +static K_MUTEX_DEFINE(z_img_sensor_lock); + +static inline int img_sensor_register(struct device *dev) +{ + int i; + + (void)k_mutex_lock(&z_img_sensor_lock, K_FOREVER); + + for (i = 0; i < z_img_sensor_num; i++) { + if (z_img_sensor_dev[i] == dev) { + k_mutex_unlock(&z_img_sensor_lock); + return -EINVAL; + } + } + if (z_img_sensor_num < CAMERA_MAX_NUMBER) { + z_img_sensor_dev[z_img_sensor_num] = dev; + z_img_sensor_num++; + + k_mutex_unlock(&z_img_sensor_lock); + + return 0; + } + + k_mutex_unlock(&z_img_sensor_lock); + + return -ENOSPC; +} + +struct device *img_sensor_get_by_id(enum camera_id id) +{ + int i; + + (void)k_mutex_lock(&z_img_sensor_lock, K_FOREVER); + for (i = 0; i < z_img_sensor_num; i++) { + struct img_sensor_data *drv_data = + z_img_sensor_dev[i]->driver_data; + + if (drv_data->host_info.id == id) { + k_mutex_unlock(&z_img_sensor_lock); + return z_img_sensor_dev[i]; + } + } + + k_mutex_unlock(&z_img_sensor_lock); + return 0; +} + +struct device *img_sensor_get_prime(void) +{ + return img_sensor_get_by_id(CAMERA_PRIMARY_ID); +} + +static const struct img_sensor_info *img_sensor_scan_one( + struct device *i2c_dev) +{ + int ret; + sys_dnode_t *node, *next_node; + + (void)k_mutex_lock(&z_img_sensor_support_lock, K_FOREVER); + + SYS_DLIST_FOR_EACH_NODE_SAFE(&z_img_sensor_support_list, + node, next_node) { + const struct img_sensor_info *sensor_info = + CONTAINER_OF(node, const struct img_sensor_info, + node); + const struct img_sensor_client *sensor_client = + &sensor_info->sensor_client; + u32_t sensor_id = 0; + + ret = i2c_write_read(i2c_dev, sensor_client->i2c_addr, + &sensor_client->id_reg, + sensor_client->w_id_reg, + &sensor_id, + sensor_client->w_sensor_id); + if (!ret && sensor_id == sensor_client->sensor_id) { + k_mutex_unlock(&z_img_sensor_support_lock); + + return sensor_info; + } + } + k_mutex_unlock(&z_img_sensor_support_lock); + + return 0; +} + +#if defined(DT_ZEPHYR_IMAGE_SENSOR_0) || defined(DT_ZEPHYR_IMAGE_SENSOR_1) +static int img_sensor_dev_init(struct device *dev) +{ + struct img_sensor_data *drv_data = dev->driver_data; + int ret = -EINVAL; + + memset(drv_data, 0, sizeof(struct img_sensor_data)); + drv_data->host_info.id = CAMERA_NULL_ID; + +#ifdef DT_ZEPHYR_IMAGE_SENSOR_0 + if (!strcmp(dev->config->name, IMAGE_SENSOR0_NAME)) { + drv_data->host_info.i2c = + device_get_binding( + DT_INST_0_ZEPHYR_IMAGE_SENSOR_BUS_NAME); + drv_data->host_info.pwr_gpio = + device_get_binding( + DT_INST_0_ZEPHYR_IMAGE_SENSOR_PWR_GPIOS_CONTROLLER); + drv_data->host_info.pin = + DT_INST_0_ZEPHYR_IMAGE_SENSOR_PWR_GPIOS_PIN; + drv_data->host_info.flag = + DT_INST_0_ZEPHYR_IMAGE_SENSOR_PWR_GPIOS_FLAGS; + ret = img_sensor_register(dev); + if (!ret) { + LOG_INF("\r\nImage sensor 0 registered.\r\n"); + } else { + LOG_ERR("\r\nImage sensor 0 un-registered.\r\n"); + } + + return ret; + } +#endif + +#ifdef DT_ZEPHYR_IMAGE_SENSOR_1 + if (!strcmp(dev->config->name, IMAGE_SENSOR1_NAME)) { + drv_data->host_info.i2c = + device_get_binding( + DT_INST_1_ZEPHYR_IMAGE_SENSOR_BUS_NAME); + drv_data->host_info.pwr_gpio = + device_get_binding( + DT_INST_1_ZEPHYR_IMAGE_SENSOR_PWR_GPIOS_CONTROLLER); + drv_data->host_info.pin = + DT_INST_1_ZEPHYR_IMAGE_SENSOR_PWR_GPIOS_PIN; + drv_data->host_info.flag = + DT_INST_1_ZEPHYR_IMAGE_SENSOR_PWR_GPIOS_FLAGS; + ret = img_sensor_register(dev); + if (!ret) { + LOG_INF("\r\nImage sensor 1 registered.\r\n"); + } else { + LOG_ERR("\r\nImage sensor 1 un-registered.\r\n"); + } + + return ret; + } +#endif + + return ret; +} +#endif + +static inline int img_sensor_power( + struct img_sensor_host *host, bool on) +{ + int ret = gpio_pin_configure(host->pwr_gpio, + host->pin, host->flag); + + if (ret) { + return -EIO; + } + + k_busy_wait(1); + + if (host->flag & GPIO_DIR_OUT) { + if (on) + ret = gpio_pin_write(host->pwr_gpio, host->pin, 1); + else + ret = gpio_pin_write(host->pwr_gpio, host->pin, 0); + + if (ret) { + return -EIO; + } + } + k_busy_wait(1000); + + return 0; +} + +static inline void img_sensor_client_dup( + struct img_sensor_client *drv_client, + const struct img_sensor_client *scan_client) +{ + memcpy((char *)drv_client, + (const char *)scan_client, sizeof(struct img_sensor_client)); +} +struct device *img_sensor_scan(enum camera_id id) +{ + int i, ret; + struct device *dev; + struct img_sensor_data *drv_data; + const struct img_sensor_info *scan_info; + + (void)k_mutex_lock(&z_img_sensor_lock, K_FOREVER); + + for (i = 0; i < z_img_sensor_num; i++) { + dev = z_img_sensor_dev[i]; + drv_data = dev->driver_data; + if (drv_data->host_info.id == CAMERA_NULL_ID) { + if (drv_data->host_info.pwr_gpio) { + ret = img_sensor_power( + &drv_data->host_info, + true); + + assert(!ret); + } + + scan_info = + img_sensor_scan_one(drv_data->host_info.i2c); + if (scan_info) { + LOG_INF("%s image sensor (id %d) is " + "probed.\r\n", + id == CAMERA_PRIMARY_ID ? + "Primary" : "Secondary", + scan_info->sensor_client.sensor_id); + img_sensor_client_dup( + &drv_data->client_info, + &scan_info->sensor_client); + + drv_data->host_info.id = id; + dev->driver_api = + scan_info->sensor_api; + + k_mutex_unlock(&z_img_sensor_lock); + + return dev; + } + if (drv_data->host_info.pwr_gpio) { + ret = img_sensor_power( + &drv_data->host_info, + false); + + assert(!ret); + } + } + } + k_mutex_unlock(&z_img_sensor_lock); + + return 0; +} + +#ifdef DT_ZEPHYR_IMAGE_SENSOR_0 +struct img_sensor_data img_sensor_data0; + +DEVICE_AND_API_INIT(img_sensor_dev0, + IMAGE_SENSOR0_NAME, img_sensor_dev_init, + &img_sensor_data0, NULL, POST_KERNEL, + CONFIG_IMAGE_SENSOR_INIT_PRIO, + 0); +#endif + +#ifdef DT_ZEPHYR_IMAGE_SENSOR_1 +struct img_sensor_data img_sensor_data1; + +DEVICE_AND_API_INIT(img_sensor_dev1, + IMAGE_SENSOR1_NAME, img_sensor_dev_init, + &img_sensor_data1, NULL, POST_KERNEL, + CONFIG_IMAGE_SENSOR_INIT_PRIO, + 0); +#endif diff --git a/drivers/camera/image_sensor_handlers.c b/drivers/camera/image_sensor_handlers.c new file mode 100644 index 000000000000..6eb38782df73 --- /dev/null +++ b/drivers/camera/image_sensor_handlers.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2019 NXP Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +Z_SYSCALL_HANDLER(img_sensor_reset, dev) +{ + return z_impl_img_sensor_reset((struct device *)dev); +} + +Z_SYSCALL_HANDLER(img_sensor_get_cap, dev, cap) +{ + return z_impl_img_sensor_get_cap((struct device *)dev, + (struct img_sensor_capability *)cap); +} + +Z_SYSCALL_HANDLER(img_sensor_read_reg, dev, + addr, addr_width, data, data_width) +{ + return z_impl_img_sensor_read_reg((struct device *)dev, + (u32_t)addr, (u8_t)addr_width, (u8_t *)data, (u8_t)data_width); +} + +Z_SYSCALL_HANDLER(img_sensor_write_reg, dev, + addr, addr_width, data, data_width) +{ + return z_impl_img_sensor_write_reg((struct device *)dev, + (u32_t)addr, (u8_t)addr_width, (u32_t)data, (u8_t)data_width); +} + +Z_SYSCALL_HANDLER(img_sensor_set_pixformat, dev, + pixformat) +{ + return z_impl_img_sensor_set_pixformat((struct device *)dev, + (enum img_sensor_pixel_format)pixformat); +} + +Z_SYSCALL_HANDLER(img_sensor_set_framesize, dev, + framesize) +{ + return z_impl_img_sensor_set_framesize((struct device *)dev, + (enum img_sensor_frame_size)framesize); +} + +Z_SYSCALL_HANDLER(img_sensor_set_contrast, dev, level) +{ + return z_impl_img_sensor_set_contrast((struct device *)dev, + (int)level); +} + +Z_SYSCALL_HANDLER(img_sensor_set_brightness, dev, level) +{ + return z_impl_img_sensor_set_brightness((struct device *)dev, + (int)level); +} + +Z_SYSCALL_HANDLER(img_sensor_set_gain, dev, + r_gain_db, g_gain_db, b_gain_db) +{ + return z_impl_img_sensor_set_gain((struct device *)dev, + (float)r_gain_db, (float)g_gain_db, (float)b_gain_db); +} + +Z_SYSCALL_HANDLER(img_sensor_set_effect, dev, effect) +{ + return z_impl_img_sensor_set_effect((struct device *)dev, + (enum img_sensor_effect)effect); +} + +Z_SYSCALL_HANDLER(img_sensor_configure, dev) +{ + return z_impl_img_sensor_configure((struct device *)dev); +} diff --git a/drivers/camera/mt9m114/CMakeLists.txt b/drivers/camera/mt9m114/CMakeLists.txt new file mode 100644 index 000000000000..eb943eb202f3 --- /dev/null +++ b/drivers/camera/mt9m114/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_MT9M114 mt9m114.c) diff --git a/drivers/camera/mt9m114/Kconfig b/drivers/camera/mt9m114/Kconfig new file mode 100644 index 000000000000..ac62b7ff9628 --- /dev/null +++ b/drivers/camera/mt9m114/Kconfig @@ -0,0 +1,17 @@ +# Kconfig - MT9M114 image sensor configuration options + +# +# Copyright (c) 2019 NXP Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +if MT9M114 + +config MT9M114_NAME + string "Driver name" + default "MT9M114" + help + Device name to identify the MT9M114 image sensor. + +endif diff --git a/drivers/camera/mt9m114/mt9m114.c b/drivers/camera/mt9m114/mt9m114.c new file mode 100644 index 000000000000..039dda1408d5 --- /dev/null +++ b/drivers/camera/mt9m114/mt9m114.c @@ -0,0 +1,558 @@ +/* mt9m114.c - driver for mt9m114 image sensor */ + +/* + * Copyright (c) 2019 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL LOG_LEVEL_ERR +#include +LOG_MODULE_REGISTER(MT9M114); + +#define MT9M114_DBG + +#undef LOG_ERR +#define LOG_ERR printk + +#ifdef MT9M114_DBG +#undef LOG_INF +#define LOG_INF printk +#endif + +#include "mt9m114.h" + +static const struct img_sensor_reg mt9m114_480_272[] = { + {MT9M114_VAR_CAM_SENSOR_CFG_Y_ADDR_START, W2B, + 0x00D4, W2B}, + /* cam_sensor_cfg_y_addr_start = 212 */ + {MT9M114_VAR_CAM_SENSOR_CFG_X_ADDR_START, W2B, + 0x00A4, W2B}, + /* cam_sensor_cfg_x_addr_start = 164 */ + {MT9M114_VAR_CAM_SENSOR_CFG_Y_ADDR_END, W2B, + 0x02FB, W2B}, + /* cam_sensor_cfg_y_addr_end = 763 */ + {MT9M114_VAR_CAM_SENSOR_CFG_X_ADDR_END, W2B, + 0x046B, W2B}, + /* cam_sensor_cfg_x_addr_end = 1131 */ + {MT9M114_VAR_CAM_SENSOR_CFG_CPIPE_LAST_ROW, W2B, + 0x0223, W2B}, + /* cam_sensor_cfg_cpipe_last_row = 547 */ + {MT9M114_VAR_CAM_CROP_WINDOW_WIDTH, W2B, + 0x03C0, W2B}, + /* cam_crop_window_width = 960 */ + {MT9M114_VAR_CAM_CROP_WINDOW_HEIGHT, W2B, + 0x0220, W2B}, + /* cam_crop_window_height = 544 */ + {MT9M114_VAR_CAM_OUTPUT_WIDTH, W2B, + 0x01E0, W2B}, + /* cam_output_width = 480 */ + {MT9M114_VAR_CAM_OUTPUT_HEIGHT, W2B, + 0x0110, W2B}, + /* cam_output_height = 272 */ + {MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_XEND, W2B, + 0x01DF, W2B}, + /* cam_stat_awb_clip_window_xend = 479 */ + {MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_YEND, W2B, + 0x010F, W2B}, + /* cam_stat_awb_clip_window_yend = 271 */ + {MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_XEND, W2B, + 0x005F, W2B}, + /* cam_stat_ae_initial_window_xend = 95 */ + {MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_YEND, W2B, + 0x0035, W2B}, + /* cam_stat_ae_initial_window_yend = 53 */ +}; + +static const struct img_sensor_reg mt9m114_init_cfg[] = { + {MT9M114_REG_LOGICAL_ADDRESS_ACCESS, W2B, + 0x1000, W2B}, + /* PLL Fout = (Fin * 2 * m) / ((n + 1) * (p + 1)) */ + {MT9M114_VAR_CAM_SYSCTL_PLL_ENABLE, W2B, + 0x01, W1B}, + /* cam_sysctl_pll_enable = 1 */ + {MT9M114_VAR_CAM_SYSCTL_PLL_DIVIDER_M_N, W2B, + 0x0120, W2B}, + /* cam_sysctl_pll_divider_m_n = 288 */ + {MT9M114_VAR_CAM_SYSCTL_PLL_DIVIDER_P, W2B, + 0x0700, W2B}, + /* cam_sysctl_pll_divider_p = 1792 */ + {MT9M114_VAR_CAM_SENSOR_CFG_PIXCLK, W2B, + 0x2DC6C00, W4B}, + /* cam_sensor_cfg_pixclk = 48000000 */ + {0x316A, W2B, + 0x8270, W2B}, + /* auto txlo_row for hot pixel and linear full well optimization */ + {0x316C, W2B, + 0x8270, W2B}, + /* auto txlo for hot pixel and linear full well optimization */ + {0x3ED0, W2B, + 0x2305, W2B}, + /* eclipse setting, ecl range=1, ecl value=2, ivln=3 */ + {0x3ED2, W2B, + 0x77CF, W2B}, + /* TX_hi=12 */ + {0x316E, W2B, + 0x8202, W2B}, + /* auto ecl , threshold 2x, ecl=0 at high gain, ecl=2 for low gain */ + {0x3180, W2B, + 0x87FF, W2B}, + /* enable delta dark */ + {0x30D4, W2B, + 0x6080, W2B}, + /* disable column correction due to AE oscillation problem */ + {0xA802, W2B, + 0x0008, W2B}, + /* RESERVED_AE_TRACK_02 */ + {0x3E14, W2B, + 0xFF39, W2B}, + /* Enabling pixout clamping to VAA during ADC streaming to + * solve column band issue + */ + {MT9M114_VAR_CAM_SENSOR_CFG_ROW_SPEED, W2B, + 0x0001, W2B}, + /* cam_sensor_cfg_row_speed = 1 */ + {MT9M114_VAR_CAM_SENSOR_CFG_FINE_INTEG_TIME_MIN, W2B, + 0x00DB, W2B}, + /* cam_sensor_cfg_fine_integ_time_min = 219 */ + {MT9M114_VAR_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX, W2B, + 0x07C2, W2B}, + /* cam_sensor_cfg_fine_integ_time_max = 1986 */ + {MT9M114_VAR_CAM_SENSOR_CFG_FRAME_LENGTH_LINES, W2B, + 0x02FE, W2B}, + /* cam_sensor_cfg_frame_length_lines = 766 */ + {MT9M114_VAR_CAM_SENSOR_CFG_LINE_LENGTH_PCK, W2B, + 0x0845, W2B}, + /* cam_sensor_cfg_line_length_pck = 2117 */ + {MT9M114_VAR_CAM_SENSOR_CFG_FINE_CORRECTION, W2B, + 0x0060, W2B}, + /* cam_sensor_cfg_fine_correction = 96 */ + {MT9M114_VAR_CAM_SENSOR_CFG_REG_0_DATA, W2B, + 0x0020, W2B}, + /* cam_sensor_cfg_reg_0_data = 32 */ + {MT9M114_VAR_CAM_SENSOR_CONTROL_READ_MODE, W2B, + 0x0000, W2B}, + /* cam_sensor_control_read_mode = 0 */ + {MT9M114_VAR_CAM_CROP_WINDOW_XOFFSET, W2B, + 0x0000, W2B}, + /* cam_crop_window_xoffset = 0 */ + {MT9M114_VAR_CAM_CROP_WINDOW_YOFFSET, W2B, + 0x0000, W2B}, + /* cam_crop_window_yoffset = 0 */ + {MT9M114_VAR_CAM_CROP_CROPMODE, W2B, + 0x03, W1B}, + /* cam_crop_cropmode = 3 */ + {MT9M114_VAR_CAM_AET_AEMODE, W2B, + 0x00, W1B}, + /* cam_aet_aemode = 0 */ + {MT9M114_VAR_CAM_AET_MAX_FRAME_RATE, W2B, + 0x1D9A, W2B}, + /* cam_aet_max_frame_rate = 7578 */ + {MT9M114_VAR_CAM_AET_MIN_FRAME_RATE, W2B, + 0x1D9A, W2B}, + /* cam_aet_min_frame_rate = 7578 */ + {MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_XSTART, W2B, + 0x0000, W2B}, + /* cam_stat_awb_clip_window_xstart = 0 */ + {MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_YSTART, W2B, + 0x0000, W2B}, + /* cam_stat_awb_clip_window_ystart = 0 */ + {MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_XSTART, W2B, + 0x0000, W2B}, + /* cam_stat_ae_initial_window_xstart = 0 */ + {MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_YSTART, W2B, + 0x0000, W2B}, + /* cam_stat_ae_initial_window_ystart = 0 */ + {MT9M114_REG_PAD_SLEW, W2B, + 0x0777, W2B}, + /* Pad slew rate */ + {MT9M114_VAR_CAM_OUTPUT_FORMAT_YUV, W2B, + 0x0038, W2B}, + /* Must set cam_output_format_yuv_clip for CSI */ +}; + +static int mt9m114_read_reg(struct device *dev, + u16_t reg_addr, u8_t *reg_data, u8_t len) +{ + int ret, i = 0; + u8_t addr_buffer[MT9M114_REG_ADDR_LEN]; + u8_t data_buffer[4]; + struct img_sensor_data *drv_data = dev->driver_data; + + assert(len <= 4); + addr_buffer[1] = reg_addr & 0xFF; + addr_buffer[0] = reg_addr >> 8; + ret = i2c_write_read(drv_data->host_info.i2c, + drv_data->client_info.i2c_addr, + addr_buffer, MT9M114_REG_ADDR_LEN, + data_buffer, len); + if (!ret) { + while (len) { + len--; + reg_data[i] = data_buffer[len]; + i++; + } + } + + return ret; +} + +static int mt9m114_write_reg(struct device *dev, + u16_t reg_addr, u32_t reg_data, u8_t len) +{ + int ret; + u8_t data[MT9M114_REG_ADDR_LEN + 4]; + u8_t data_len = len; + struct img_sensor_data *drv_data = dev->driver_data; + + assert(len <= 4); + data[1] = reg_addr & 0xFF; + data[0] = reg_addr >> 8; + while (data_len) { + data_len--; + data[data_len + MT9M114_REG_ADDR_LEN] = (u8_t)reg_data; + reg_data >>= 8; + } + ret = i2c_write(drv_data->host_info.i2c, (const u8_t *)data, + MT9M114_REG_ADDR_LEN + len, + drv_data->client_info.i2c_addr); + + return ret; +} + +static int mt9m114_read_reg_cb(struct device *dev, + u32_t reg_addr, u8_t reg_len __unused, u8_t *reg_data, u8_t data_len) +{ + return mt9m114_read_reg(dev, (u16_t)reg_addr, reg_data, data_len); +} + +static int mt9m114_write_reg_cb(struct device *dev, + u32_t reg_addr, u8_t reg_len __unused, u32_t reg_data, u8_t data_len) +{ + return mt9m114_write_reg(dev, (u16_t)reg_addr, reg_data, data_len); +} + +static int mt9m114_modify_reg(struct device *dev, + u32_t reg, u8_t data_width, u32_t clr_msk, u32_t value) +{ + int status; + u32_t regval; + + if (data_width != 1 && data_width != 2 && data_width != 4) { + return -EINVAL; + } + + status = mt9m114_read_reg(dev, reg, (u8_t *)®val, data_width); + if (status) { + return status; + } + + regval = (regval & ~(clr_msk)) | (value & clr_msk); + + status = mt9m114_write_reg(dev, reg, regval, data_width); + return status; +} + +static int mt9m114_soft_reset(struct device *dev) +{ + int ret = mt9m114_modify_reg(dev, + MT9M114_REG_RESET_AND_MISC_CONTROL, 2u, 0x01, 0x01); + + if (ret) { + return ret; + } + + k_busy_wait(1000); + ret = mt9m114_modify_reg(dev, + MT9M114_REG_RESET_AND_MISC_CONTROL, 2u, 0x01, 0x00); + k_busy_wait(45 * 1000); + + return ret; +} + +static int mt9m114_multi_write(struct device *dev, + const struct img_sensor_reg regs[], int num) +{ + int status = 0, i; + + for (i = 0; i < num; i++) { + status = mt9m114_write_reg(dev, regs[i].reg, + regs[i].value, (u8_t)regs[i].w_value); + if (status) { + break; + } + } + return status; +} + +static int mt9m114_set_state(struct device *dev, + u8_t next_state) +{ + u16_t value; + int ret; + + /* Set the desired next state. */ + ret = mt9m114_write_reg(dev, MT9M114_VAR_SYSMGR_NEXT_STATE, + next_state, 1); + if (ret) { + return ret; + } + + /* Check that the FW is ready to accept a new command. */ + while (1) { + k_busy_wait(100); + ret = mt9m114_read_reg(dev, + MT9M114_REG_COMMAND_REGISTER, + (u8_t *)&value, 2); + if (ret) + return ret; + if (!(value & MT9M114_COMMAND_SET_STATE)) { + break; + } + } + + /* Issue the Set State command. */ + ret = mt9m114_write_reg(dev, MT9M114_REG_COMMAND_REGISTER, + MT9M114_COMMAND_SET_STATE | MT9M114_COMMAND_OK, 2); + if (ret) { + return ret; + } + + /* Wait for the FW to complete the command. */ + while (1) { + k_busy_wait(100); + ret = mt9m114_read_reg(dev, + MT9M114_REG_COMMAND_REGISTER, + (u8_t *)&value, 2); + if (ret) { + return ret; + } + if (!(value & MT9M114_COMMAND_SET_STATE)) { + break; + } + } + + /* Check the 'OK' bit to see if the command was successful. */ + ret = mt9m114_read_reg(dev, + MT9M114_REG_COMMAND_REGISTER, + (u8_t *)&value, 2); + if (ret) { + return ret; + } + if (!(value & MT9M114_COMMAND_OK)) { + return -EIO; + } + + return ret; +} + +static int mt9m114_reset(struct device *dev) +{ + int ret; + + /* SW reset. */ + ret = mt9m114_soft_reset(dev); + if (ret) { + return ret; + } + + ret = mt9m114_multi_write(dev, mt9m114_init_cfg, + ARRAY_SIZE(mt9m114_init_cfg)); + if (ret) { + return ret; + } + + return ret; +} + +static int mt9m114_set_config(struct device *dev) +{ + struct img_sensor_data *drv_data = dev->driver_data; + u16_t pixformat = 0; + int ret; + + /* Pixel format. */ + if (PIXEL_FORMAT_RGB_565 == + drv_data->client_info.pixformat) { + pixformat |= ((1U << 8U) | (1U << 1U)); + } + + ret = mt9m114_write_reg(dev, + MT9M114_VAR_CAM_OUTPUT_FORMAT, pixformat, 2); + if (ret) { + return ret; + } + + ret = mt9m114_write_reg(dev, + MT9M114_VAR_CAM_PORT_OUTPUT_CONTROL, 0x8000, 2); + if (ret) { + return ret; + } + + /* Resolution 480 * 272*/ + if (drv_data->client_info.width == MT9M114_DEFAULT_WIDTH && + drv_data->client_info.height == MT9M114_DEFAULT_HEIGHT) { + ret = mt9m114_multi_write(dev, + mt9m114_480_272, ARRAY_SIZE(mt9m114_480_272)); + if (ret) { + return ret; + } + } else { + LOG_ERR("MT9M114: other than 480X272 not implemented\r\n"); + return -EINVAL; + } + + /* Execute Change-Config command. */ + ret = mt9m114_set_state(dev, MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE); + if (ret) { + return ret; + } + + ret = mt9m114_set_state(dev, MT9M114_SYS_STATE_START_STREAMING); + + return ret; +} + +static int mt9m114_set_pixformat(struct device *dev, + enum display_pixel_format pixformat) +{ + struct img_sensor_data *drv_data = dev->driver_data; + + if (pixformat != PIXEL_FORMAT_RGB_565) { + LOG_ERR("Other than RGB565 not implemented on mt9m114\r\n"); + return -EINVAL; + } + drv_data->client_info.pixformat = pixformat; + + return 0; +} + +static int mt9m114_set_framesize(struct device *dev, + u16_t width, u16_t height) +{ + struct img_sensor_data *drv_data = dev->driver_data; + + if (width != MT9M114_DEFAULT_WIDTH || + height != MT9M114_DEFAULT_HEIGHT) { + LOG_ERR("Other than 480X272 not implemented" + " on mt9m114 %d X %d\r\n", + width, height); + return -EINVAL; + } + drv_data->client_info.width = width; + drv_data->client_info.height = height; + + return 0; +} + +static int mt9m114_set_contrast(struct device *dev, int level) +{ + struct img_sensor_data *drv_data = dev->driver_data; + + drv_data->client_info.contrast_level = level; + + return 0; +} + +static int mt9m114_set_brightness(struct device *dev, int level) +{ + struct img_sensor_data *drv_data = dev->driver_data; + + drv_data->client_info.bright_level = level; + + return 0; +} + +static int mt9m114_set_effect(struct device *dev, enum img_sensor_effect effect) +{ + struct img_sensor_data *drv_data = dev->driver_data; + + drv_data->client_info.effect = effect; + + return 0; +} + +static int mt9m114_set_gain(struct device *dev, float r_gain_db, + float g_gain_db, float b_gain_db) +{ + struct img_sensor_data *drv_data = dev->driver_data; + + drv_data->client_info.r_gain_db = r_gain_db; + drv_data->client_info.g_gain_db = g_gain_db; + drv_data->client_info.b_gain_db = b_gain_db; + + return 0; +} + +static int mt9m114_get_cap(struct device *dev, + struct img_sensor_capability *cap) +{ + struct img_sensor_data *drv_data = dev->driver_data; + + cap->pixformat_support = + drv_data->client_info.cap.pixformat_support; + cap->width_max = drv_data->client_info.cap.width_max; + cap->height_max = drv_data->client_info.cap.height_max; + + return 0; +} + + +const struct img_sensor_driver_api mt9m114_api = { + .img_sensor_reset_cb = mt9m114_reset, + .img_sensor_get_cap_cb = mt9m114_get_cap, + .img_sensor_read_reg_cb = mt9m114_read_reg_cb, + .img_sensor_write_reg_cb = mt9m114_write_reg_cb, + .img_sensor_set_pixformat_cb = mt9m114_set_pixformat, + .img_sensor_set_framesize_cb = mt9m114_set_framesize, + .img_sensor_set_contrast_cb = mt9m114_set_contrast, + .img_sensor_set_brightness_cb = mt9m114_set_brightness, + .img_sensor_set_rgb_gain_cb = mt9m114_set_gain, + .img_sensor_set_effect_cb = mt9m114_set_effect, + .img_sensor_config_cb = mt9m114_set_config, +}; + +struct img_sensor_info mt9m114_info = { + .sensor_client = { + .i2c_addr = MT9M114_I2C_ADDR, + .sensor_id = MT9M114_CHIP_ID, + .w_sensor_id = W1B, + .id_reg = MT9M114_REG_CHIP_ID, + .w_id_reg = W2B, + .width = MT9M114_DEFAULT_WIDTH, + .height = MT9M114_DEFAULT_HEIGHT, + .pixformat = PIXEL_FORMAT_RGB_565, + .cap = { + .width_max = MT9M114_MAX_WIDTH, + .height_max = MT9M114_MAX_HEIGHT, + .pixformat_support = ( + PIXEL_FORMAT_RGB_565 | + PIXEL_FORMAT_YUV_420 | + PIXEL_FORMAT_YUV_422), + }, + }, + .sensor_api = &mt9m114_api, +}; + +static int mt9m114_dev_init(struct device *dev) +{ + struct img_sensor_info *drv_data = dev->driver_data; + + img_sensor_support_add(drv_data); + + return 0; +} + +DEVICE_AND_API_INIT(mt9m114_dev, + CONFIG_MT9M114_NAME, mt9m114_dev_init, + &mt9m114_info, NULL, POST_KERNEL, + CONFIG_IMAGE_SENSOR_INIT_PRIO, + 0); + diff --git a/drivers/camera/mt9m114/mt9m114.h b/drivers/camera/mt9m114/mt9m114.h new file mode 100644 index 000000000000..295d43b9f201 --- /dev/null +++ b/drivers/camera/mt9m114/mt9m114.h @@ -0,0 +1,614 @@ +/* mt9m114.h - header file for mt9m114 image sensor driver */ + +/* + * Copyright (c) 2019 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_IMAGE_SENSOR_MT9M114_H_ +#define ZEPHYR_DRIVERS_IMAGE_SENSOR_MT9M114_H_ + +#include +#include +#include + +#define MT9M114_MAX_WIDTH 1296 +#define MT9M114_MAX_HEIGHT 976 + +#define MT9M114_DEFAULT_WIDTH 480 +#define MT9M114_DEFAULT_HEIGHT 272 + + +#define MT9M114_CHIP_ID 0x24 +#define MT9M114_I2C_ADDR 0x48 + +#define MT9M114_REG_ADDR_LEN W2B + +/* 1.Core registers */ +#define MT9M114_REG_Y_ADDR_START 0x3002 +#define MT9M114_REG_X_ADDR_START 0x3004 +#define MT9M114_REG_Y_ADDR_END 0x3006 +#define MT9M114_REG_X_ADDR_END 0x3008 +#define MT9M114_REG_FRAME_LENGTH_LINES 0x300A +#define MT9M114_REG_LINE_LENGTH_PCK_ 0x300C +#define MT9M114_REG_COARSE_INTEGRATION_TIME 0x3012 +#define MT9M114_REG_FINE_INTEGRATION_TIME 0x3014 +#define MT9M114_REG_RESET_REGISTER 0x301A +#define MT9M114_REG_FLASH 0x3046 +#define MT9M114_REG_FLASH_COUNT 0x3048 +#define MT9M114_REG_GREEN1_GAIN 0x3056 +#define MT9M114_REG_BLUE_GAIN 0x3058 +#define MT9M114_REG_RED_GAIN 0x305A +#define MT9M114_REG_GREEN2_GAIN 0x305C +#define MT9M114_REG_GLOBAL_GAIN 0x305E +#define MT9M114_REG_FUSE_ID1 0x31F4 +#define MT9M114_REG_FUSE_ID2 0x31F6 +#define MT9M114_REG_FUSE_ID3 0x31F8 +#define MT9M114_REG_FUSE_ID4 0x31FA +#define MT9M114_REG_CHAIN_CONTROL 0x31FC +#define MT9M114_REG_CUSTOMER_REV 0x31FE + +/* 2.SOC1 registers */ +#define MT9M114_REG_COLOR_PIPELINE_CONTROL 0x3210 + +/* 3.SOC2 registers */ +#define MT9M114_REG_P_G1_P0Q0 0x3640 +#define MT9M114_REG_P_G1_P0Q1 0x3642 +#define MT9M114_REG_P_G1_P0Q2 0x3644 +#define MT9M114_REG_P_G1_P0Q3 0x3646 +#define MT9M114_REG_P_G1_P0Q4 0x3648 +#define MT9M114_REG_P_R_P0Q0 0x364A +#define MT9M114_REG_P_R_P0Q1 0x364C +#define MT9M114_REG_P_R_P0Q2 0x364E +#define MT9M114_REG_P_R_P0Q3 0x3650 +#define MT9M114_REG_P_R_P0Q4 0x3652 +#define MT9M114_REG_P_B_P0Q0 0x3654 +#define MT9M114_REG_P_B_P0Q1 0x3656 +#define MT9M114_REG_P_B_P0Q2 0x3658 +#define MT9M114_REG_P_B_P0Q3 0x365A +#define MT9M114_REG_P_B_P0Q4 0x365C +#define MT9M114_REG_P_G2_P0Q0 0x365E +#define MT9M114_REG_P_G2_P0Q1 0x3660 +#define MT9M114_REG_P_G2_P0Q2 0x3662 +#define MT9M114_REG_P_G2_P0Q3 0x3664 +#define MT9M114_REG_P_G2_P0Q4 0x3666 +#define MT9M114_REG_P_G1_P1Q0 0x3680 +#define MT9M114_REG_P_G1_P1Q1 0x3682 +#define MT9M114_REG_P_G1_P1Q2 0x3684 +#define MT9M114_REG_P_G1_P1Q3 0x3686 +#define MT9M114_REG_P_G1_P1Q4 0x3688 +#define MT9M114_REG_P_R_P1Q0 0x368A +#define MT9M114_REG_P_R_P1Q1 0x368C +#define MT9M114_REG_P_R_P1Q2 0x368E +#define MT9M114_REG_P_R_P1Q3 0x3690 +#define MT9M114_REG_P_R_P1Q4 0x3692 +#define MT9M114_REG_P_B_P1Q0 0x3694 +#define MT9M114_REG_P_B_P1Q1 0x3696 +#define MT9M114_REG_P_B_P1Q2 0x3698 +#define MT9M114_REG_P_B_P1Q3 0x369A +#define MT9M114_REG_P_B_P1Q4 0x369C +#define MT9M114_REG_P_G2_P1Q0 0x369E +#define MT9M114_REG_P_G2_P1Q1 0x36A0 +#define MT9M114_REG_P_G2_P1Q2 0x36A2 +#define MT9M114_REG_P_G2_P1Q3 0x36A4 +#define MT9M114_REG_P_G2_P1Q4 0x36A6 +#define MT9M114_REG_P_G1_P2Q0 0x36C0 +#define MT9M114_REG_P_G1_P2Q1 0x36C2 +#define MT9M114_REG_P_G1_P2Q2 0x36C4 +#define MT9M114_REG_P_G1_P2Q3 0x36C6 +#define MT9M114_REG_P_G1_P2Q4 0x36C8 +#define MT9M114_REG_P_R_P2Q0 0x36CA +#define MT9M114_REG_P_R_P2Q1 0x36CC +#define MT9M114_REG_P_R_P2Q2 0x36CE +#define MT9M114_REG_P_R_P2Q3 0x36D0 +#define MT9M114_REG_P_R_P2Q4 0x36D2 +#define MT9M114_REG_P_B_P2Q0 0x36D4 +#define MT9M114_REG_P_B_P2Q1 0x36D6 +#define MT9M114_REG_P_B_P2Q2 0x36D8 +#define MT9M114_REG_P_B_P2Q3 0x36DA +#define MT9M114_REG_P_B_P2Q4 0x36DC +#define MT9M114_REG_P_G2_P2Q0 0x36DE +#define MT9M114_REG_P_G2_P2Q1 0x36E0 +#define MT9M114_REG_P_G2_P2Q2 0x36E2 +#define MT9M114_REG_P_G2_P2Q3 0x36E4 +#define MT9M114_REG_P_G2_P2Q4 0x36E6 +#define MT9M114_REG_P_G1_P3Q0 0x3700 +#define MT9M114_REG_P_G1_P3Q1 0x3702 +#define MT9M114_REG_P_G1_P3Q2 0x3704 +#define MT9M114_REG_P_G1_P3Q3 0x3706 +#define MT9M114_REG_P_G1_P3Q4 0x3708 +#define MT9M114_REG_P_R_P3Q0 0x370A +#define MT9M114_REG_P_R_P3Q1 0x370C +#define MT9M114_REG_P_R_P3Q2 0x370E +#define MT9M114_REG_P_R_P3Q3 0x3710 +#define MT9M114_REG_P_R_P3Q4 0x3712 +#define MT9M114_REG_P_B_P3Q0 0x3714 +#define MT9M114_REG_P_B_P3Q1 0x3716 +#define MT9M114_REG_P_B_P3Q2 0x3718 +#define MT9M114_REG_P_B_P3Q3 0x371A +#define MT9M114_REG_P_B_P3Q4 0x371C +#define MT9M114_REG_P_G2_P3Q0 0x371E +#define MT9M114_REG_P_G2_P3Q1 0x3720 +#define MT9M114_REG_P_G2_P3Q2 0x3722 +#define MT9M114_REG_P_G2_P3Q3 0x3724 +#define MT9M114_REG_P_G2_P3Q4 0x3726 +#define MT9M114_REG_P_G1_P4Q0 0x3740 +#define MT9M114_REG_P_G1_P4Q1 0x3742 +#define MT9M114_REG_P_G1_P4Q2 0x3744 +#define MT9M114_REG_P_G1_P4Q3 0x3746 +#define MT9M114_REG_P_G1_P4Q4 0x3748 +#define MT9M114_REG_P_R_P4Q0 0x374A +#define MT9M114_REG_P_R_P4Q1 0x374C +#define MT9M114_REG_P_R_P4Q2 0x374E +#define MT9M114_REG_P_R_P4Q3 0x3750 +#define MT9M114_REG_P_R_P4Q4 0x3752 +#define MT9M114_REG_P_B_P4Q0 0x3754 +#define MT9M114_REG_P_B_P4Q1 0x3756 +#define MT9M114_REG_P_B_P4Q2 0x3758 +#define MT9M114_REG_P_B_P4Q3 0x375A +#define MT9M114_REG_P_B_P4Q4 0x375C +#define MT9M114_REG_P_G2_P4Q0 0x375E +#define MT9M114_REG_P_G2_P4Q1 0x3760 +#define MT9M114_REG_P_G2_P4Q2 0x3762 +#define MT9M114_REG_P_G2_P4Q3 0x3764 +#define MT9M114_REG_P_G2_P4Q4 0x3766 +#define MT9M114_REG_CENTER_ROW 0x3782 +#define MT9M114_REG_CENTER_COLUMN 0x3784 + +/* 4.SYSCTL registers */ +#define MT9M114_REG_CHIP_ID 0x0000 +#define MT9M114_REG_CLOCKS_CONTROL 0x0016 +#define MT9M114_REG_RESET_AND_MISC_CONTROL 0x001A +#define MT9M114_REG_PAD_SLEW 0x001E +#define MT9M114_REG_USER_DEFINED_DEVICE_ADDRESS_ID 0x002E +#define MT9M114_REG_PAD_CONTROL 0x0032 +#define MT9M114_REG_COMMAND_REGISTER 0x0080 + +/* 5.XDMA registers */ +#define MT9M114_REG_ACCESS_CTL_STAT 0x0982 +#define MT9M114_REG_PHYSICAL_ADDRESS_ACCESS 0x098A +#define MT9M114_REG_LOGICAL_ADDRESS_ACCESS 0x098E +#define MT9M114_REG_MCU_VARIABLE_DATA0 0x0990 +#define MT9M114_REG_MCU_VARIABLE_DATA1 0x0992 +#define MT9M114_REG_MCU_VARIABLE_DATA2 0x0994 +#define MT9M114_REG_MCU_VARIABLE_DATA3 0x0996 +#define MT9M114_REG_MCU_VARIABLE_DATA4 0x0998 +#define MT9M114_REG_MCU_VARIABLE_DATA5 0x099A +#define MT9M114_REG_MCU_VARIABLE_DATA6 0x099C +#define MT9M114_REG_MCU_VARIABLE_DATA7 0x099E + +/*! @brief MT9M114 variables definitions.*/ + +/* 01.Monitor variables */ +#define MT9M114_VAR_MON_MAJOR_VERSION 0x8000 +#define MT9M114_VAR_MON_MINOR_VERSION 0x8002 +#define MT9M114_VAR_MON_RELEASE_VERSION 0x8004 +#define MT9M114_VAR_MON_HEARTBEAT 0x8006 + +/* 02.Sequencer variables */ +#define MT9M114_VAR_SEQ_ERROR_CODE 0x8406 + +/* 03.AE_Rule variables */ +#define MT9M114_VAR_AE_RULE_ALGO 0xA404 +#define MT9M114_VAR_AE_RULE_AVG_Y_FROM_STATS 0xA406 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_0_0 0xA407 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_0_1 0xA408 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_0_2 0xA409 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_0_3 0xA40A +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_0_4 0xA40B +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_1_0 0xA40C +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_1_1 0xA40D +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_1_2 0xA40E +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_1_3 0xA40F +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_1_4 0xA410 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_2_0 0xA411 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_2_1 0xA412 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_2_2 0xA413 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_2_3 0xA414 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_2_4 0xA415 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_3_0 0xA416 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_3_1 0xA417 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_3_2 0xA418 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_3_3 0xA419 +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_3_4 0xA41A +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_4_0 0xA41B +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_4_1 0xA41C +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_4_2 0xA41D +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_4_3 0xA41E +#define MT9M114_VAR_AE_RULE_AE_WEIGHT_TABLE_4_4 0xA41F +#define MT9M114_VAR_AE_RULE_AE_ADAPTIVE_STRENGTH 0xA420 + +/* 04.AE_Track variables */ +#define MT9M114_VAR_AE_TRACK_STATUS 0xA800 +#define MT9M114_VAR_AE_TRACK_ALGO 0xA804 +#define MT9M114_VAR_AE_TRACK_TARGET_AVERAGE_LUMA 0xA807 +#define MT9M114_VAR_AE_TRACK_GATE_PERCENTAGE 0xA808 +#define MT9M114_VAR_AE_TRACK_CURRENT_AVERAGE_LUMA 0xA809 +#define MT9M114_VAR_AE_TRACK_AE_TRACKING_DAMPENING_SPEED 0xA80A +#define MT9M114_VAR_AE_TRACK_AE_DAMPENING_SPEED 0xA80B +#define MT9M114_VAR_AE_TRACK_SKIP_FRAMES_COUNTER 0xA80D +#define MT9M114_VAR_AE_TRACK_CURRENT_FLICKER_LINES 0xA80E +#define MT9M114_VAR_AE_TRACK_FDZONE 0xA818 +#define MT9M114_VAR_AE_TRACK_ZONE 0xA81B +#define MT9M114_VAR_AE_TRACK_FLICKER_LINES_50HZ 0xA826 +#define MT9M114_VAR_AE_TRACK_VIRT_EXPOSURE_LOG 0xA828 +#define MT9M114_VAR_AE_TRACK_MIN_VIRT_EXPOSURE_LOG_ZONE0 0xA82A +#define MT9M114_VAR_AE_TRACK_MAX_VIRT_EXPOSURE_LOG_ZONE0 0xA82C +#define MT9M114_VAR_AE_TRACK_MAX_VIRT_EXPOSURE_LOG_ZONE1 0xA82E +#define MT9M114_VAR_AE_TRACK_VIRT_GAIN 0xA838 + +/* 05.AWB variables */ +#define MT9M114_VAR_AWB_STATUS 0xAC00 +#define MT9M114_VAR_AWB_MODE 0xAC02 +#define MT9M114_VAR_AWB_R_RATIO_LOWER 0xAC06 +#define MT9M114_VAR_AWB_R_RATIO_UPPER 0xAC07 +#define MT9M114_VAR_AWB_B_RATIO_LOWER 0xAC08 +#define MT9M114_VAR_AWB_B_RATIO_UPPER 0xAC09 +#define MT9M114_VAR_AWB_R_SCENE_RATIO_LOWER 0xAC0A +#define MT9M114_VAR_AWB_R_SCENE_RATIO_UPPER 0xAC0B +#define MT9M114_VAR_AWB_B_SCENE_RATIO_LOWER 0xAC0C +#define MT9M114_VAR_AWB_B_SCENE_RATIO_UPPER 0xAC0D +#define MT9M114_VAR_AWB_R_RATIO_PRE_AWB 0xAC0E +#define MT9M114_VAR_AWB_B_RATIO_PRE_AWB 0xAC0F +#define MT9M114_VAR_AWB_R_GAIN 0xAC12 +#define MT9M114_VAR_AWB_B_GAIN 0xAC14 +#define MT9M114_VAR_AWB_PRE_AWB_RATIOS_TRACKING_SPEED 0xAC16 +#define MT9M114_VAR_AWB_PIXEL_THRESHOLD_COUNT 0xAC18 + +/* 06.BlackLevel variables */ +#define MT9M114_VAR_BLACKLEVEL_ALGO 0xB004 +#define MT9M114_VAR_BLACKLEVEL_MAX_BLACK_LEVEL 0xB00C +#define MT9M114_VAR_BLACKLEVEL_BLACK_LEVEL_DAMPENING 0xB00D + +/* 07.CCM variables */ +#define MT9M114_VAR_CCM_ALGO 0xB404 +#define MT9M114_VAR_CCM_0 0xB406 +#define MT9M114_VAR_CCM_1 0xB408 +#define MT9M114_VAR_CCM_2 0xB40A +#define MT9M114_VAR_CCM_3 0xB40C +#define MT9M114_VAR_CCM_4 0xB40E +#define MT9M114_VAR_CCM_5 0xB410 +#define MT9M114_VAR_CCM_6 0xB412 +#define MT9M114_VAR_CCM_7 0xB414 +#define MT9M114_VAR_CCM_8 0xB416 +#define MT9M114_VAR_CCM_LL_DELTA_CCM_0 0xB418 +#define MT9M114_VAR_CCM_LL_DELTA_CCM_1 0xB41A +#define MT9M114_VAR_CCM_LL_DELTA_CCM_2 0xB41C +#define MT9M114_VAR_CCM_LL_DELTA_CCM_3 0xB41E +#define MT9M114_VAR_CCM_LL_DELTA_CCM_4 0xB420 +#define MT9M114_VAR_CCM_LL_DELTA_CCM_5 0xB422 +#define MT9M114_VAR_CCM_LL_DELTA_CCM_6 0xB424 +#define MT9M114_VAR_CCM_LL_DELTA_CCM_7 0xB426 +#define MT9M114_VAR_CCM_LL_DELTA_CCM_8 0xB428 +#define MT9M114_VAR_CCM_DELTA_GAIN 0xB42A +#define MT9M114_VAR_CCM_DELTA_THRESH 0xB42B + +/* 08.LowLight variables */ +#define MT9M114_VAR_LL_MODE 0xBC02 +#define MT9M114_VAR_LL_ALGO 0xBC04 +#define MT9M114_VAR_LL_GAMMA_SELECT 0xBC07 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_0 0xBC0A +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_1 0xBC0B +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_2 0xBC0C +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_3 0xBC0D +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_4 0xBC0E +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_5 0xBC0F +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_6 0xBC10 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_7 0xBC11 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_8 0xBC12 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_9 0xBC13 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_10 0xBC14 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_11 0xBC15 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_12 0xBC16 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_13 0xBC17 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_14 0xBC18 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_15 0xBC19 +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_16 0xBC1A +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_17 0xBC1B +#define MT9M114_VAR_LL_GAMMA_CONTRAST_CURVE_18 0xBC1C +#define MT9M114_VAR_LL_GAMMA_NRCURVE_0 0xBC1D +#define MT9M114_VAR_LL_GAMMA_NRCURVE_1 0xBC1E +#define MT9M114_VAR_LL_GAMMA_NRCURVE_2 0xBC1F +#define MT9M114_VAR_LL_GAMMA_NRCURVE_3 0xBC20 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_4 0xBC21 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_5 0xBC22 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_6 0xBC23 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_7 0xBC24 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_8 0xBC25 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_9 0xBC26 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_10 0xBC27 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_11 0xBC28 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_12 0xBC29 +#define MT9M114_VAR_LL_GAMMA_NRCURVE_13 0xBC2A +#define MT9M114_VAR_LL_GAMMA_NRCURVE_14 0xBC2B +#define MT9M114_VAR_LL_GAMMA_NRCURVE_15 0xBC2C +#define MT9M114_VAR_LL_GAMMA_NRCURVE_16 0xBC2D +#define MT9M114_VAR_LL_GAMMA_NRCURVE_17 0xBC2E +#define MT9M114_VAR_LL_GAMMA_NRCURVE_18 0xBC2F +#define MT9M114_VAR_LL_BM_PRECISION_BITS 0xBC31 +#define MT9M114_VAR_LL_AVERAGE_LUMA_FADE_TO_BLACK 0xBC3A +#define MT9M114_VAR_LL_FADE_TO_BLACK_DAMPENING_SPEED 0xBC3C + +/* 09.CameraControl variables */ +#define MT9M114_VAR_CAM_SENSOR_CFG_Y_ADDR_START 0xC800 +#define MT9M114_VAR_CAM_SENSOR_CFG_X_ADDR_START 0xC802 +#define MT9M114_VAR_CAM_SENSOR_CFG_Y_ADDR_END 0xC804 +#define MT9M114_VAR_CAM_SENSOR_CFG_X_ADDR_END 0xC806 +#define MT9M114_VAR_CAM_SENSOR_CFG_PIXCLK 0xC808 +#define MT9M114_VAR_CAM_SENSOR_CFG_ROW_SPEED 0xC80C +#define MT9M114_VAR_CAM_SENSOR_CFG_FINE_INTEG_TIME_MIN 0xC80E +#define MT9M114_VAR_CAM_SENSOR_CFG_FINE_INTEG_TIME_MAX 0xC810 +#define MT9M114_VAR_CAM_SENSOR_CFG_FRAME_LENGTH_LINES 0xC812 +#define MT9M114_VAR_CAM_SENSOR_CFG_LINE_LENGTH_PCK 0xC814 +#define MT9M114_VAR_CAM_SENSOR_CFG_FINE_CORRECTION 0xC816 +#define MT9M114_VAR_CAM_SENSOR_CFG_CPIPE_LAST_ROW 0xC818 +#define MT9M114_VAR_CAM_SENSOR_CFG_REG_0_DATA 0xC826 +#define MT9M114_VAR_CAM_SENSOR_CONTROL_READ_MODE 0xC834 +#define MT9M114_VAR_CAM_SENSOR_CONTROL_ANALOG_GAIN 0xC836 +#define MT9M114_VAR_CAM_SENSOR_CONTROL_VIRT_COLUMN_GAIN 0xC838 +#define MT9M114_VAR_CAM_SENSOR_CONTROL_FRAME_LENGTH_LINES 0xC83A +#define MT9M114_VAR_CAM_SENSOR_CONTROL_COARSE_INTEGRATION_TIME 0xC83C +#define MT9M114_VAR_CAM_SENSOR_CONTROL_FINE_INTEGRATION_TIME 0xC83E +#define MT9M114_VAR_CAM_CPIPE_CONTROL_DGAIN_RED 0xC840 +#define MT9M114_VAR_CAM_CPIPE_CONTROL_DGAIN_GREEN1 0xC842 +#define MT9M114_VAR_CAM_CPIPE_CONTROL_DGAIN_GREEN2 0xC844 +#define MT9M114_VAR_CAM_CPIPE_CONTROL_DGAIN_BLUE 0xC846 +#define MT9M114_VAR_CAM_CPIPE_CONTROL_DGAIN_SECOND 0xC848 +#define MT9M114_VAR_CAM_CPIPE_CONTROL_SECOND_BLACK_LEVEL 0xC84B +#define MT9M114_VAR_CAM_MODE_SELECT 0xC84C +#define MT9M114_VAR_CAM_MODE_TEST_PATTERN_SELECT 0xC84D +#define MT9M114_VAR_CAM_MODE_TEST_PATTERN_RED 0xC84E +#define MT9M114_VAR_CAM_MODE_TEST_PATTERN_GREEN 0xC850 +#define MT9M114_VAR_CAM_MODE_TEST_PATTERN_BLUE 0xC852 +#define MT9M114_VAR_CAM_CROP_WINDOW_XOFFSET 0xC854 +#define MT9M114_VAR_CAM_CROP_WINDOW_YOFFSET 0xC856 +#define MT9M114_VAR_CAM_CROP_WINDOW_WIDTH 0xC858 +#define MT9M114_VAR_CAM_CROP_WINDOW_HEIGHT 0xC85A +#define MT9M114_VAR_CAM_CROP_CROPMODE 0xC85C +#define MT9M114_VAR_CAM_SCALE_VERTICAL_TC_MODE 0xC85E +#define MT9M114_VAR_CAM_SCALE_VERTICAL_TC_PERCENTAGE 0xC860 +#define MT9M114_VAR_CAM_SCALE_VERTICAL_TC_STRETCH_FACTOR 0xC862 +#define MT9M114_VAR_CAM_OUTPUT_WIDTH 0xC868 +#define MT9M114_VAR_CAM_OUTPUT_HEIGHT 0xC86A +#define MT9M114_VAR_CAM_OUTPUT_FORMAT 0xC86C +#define MT9M114_VAR_CAM_OUTPUT_FORMAT_YUV 0xC86E +#define MT9M114_VAR_CAM_OUTPUT_Y_OFFSET 0xC870 +#define MT9M114_VAR_CAM_HUE_ANGLE 0xC873 +#define MT9M114_VAR_CAM_SFX_CONTROL 0xC874 +#define MT9M114_VAR_CAM_SFX_SOLARIZATION_THRESH 0xC875 +#define MT9M114_VAR_CAM_SFX_SEPIA_CR 0xC876 +#define MT9M114_VAR_CAM_SFX_SEPIA_CB 0xC877 +#define MT9M114_VAR_CAM_AET_AEMODE 0xC878 +#define MT9M114_VAR_CAM_AET_SKIP_FRAMES 0xC879 +#define MT9M114_VAR_CAM_AET_TARGET_AVERAGE_LUMA 0xC87A +#define MT9M114_VAR_CAM_AET_TARGET_AVERAGE_LUMA_DARK 0xC87B +#define MT9M114_VAR_CAM_AET_BLACK_CLIPPING_TARGET 0xC87C +#define MT9M114_VAR_CAM_AET_AE_MIN_VIRT_INT_TIME_PCLK 0xC87E +#define MT9M114_VAR_CAM_AET_AE_MIN_VIRT_DGAIN 0xC880 +#define MT9M114_VAR_CAM_AET_AE_MAX_VIRT_DGAIN 0xC882 +#define MT9M114_VAR_CAM_AET_AE_MIN_VIRT_AGAIN 0xC884 +#define MT9M114_VAR_CAM_AET_AE_MAX_VIRT_AGAIN 0xC886 +#define MT9M114_VAR_CAM_AET_AE_VIRT_GAIN_TH_EG 0xC888 +#define MT9M114_VAR_CAM_AET_AE_EG_GATE_PERCENTAGE 0xC88A +#define MT9M114_VAR_CAM_AET_FLICKER_FREQ_HZ 0xC88B +#define MT9M114_VAR_CAM_AET_MAX_FRAME_RATE 0xC88C +#define MT9M114_VAR_CAM_AET_MIN_FRAME_RATE 0xC88E +#define MT9M114_VAR_CAM_AET_TARGET_GAIN 0xC890 +#define MT9M114_VAR_CAM_AWB_CCM_L_0 0xC892 +#define MT9M114_VAR_CAM_AWB_CCM_L_1 0xC894 +#define MT9M114_VAR_CAM_AWB_CCM_L_2 0xC896 +#define MT9M114_VAR_CAM_AWB_CCM_L_3 0xC898 +#define MT9M114_VAR_CAM_AWB_CCM_L_4 0xC89A +#define MT9M114_VAR_CAM_AWB_CCM_L_5 0xC89C +#define MT9M114_VAR_CAM_AWB_CCM_L_6 0xC89E +#define MT9M114_VAR_CAM_AWB_CCM_L_7 0xC8A0 +#define MT9M114_VAR_CAM_AWB_CCM_L_8 0xC8A2 +#define MT9M114_VAR_CAM_AWB_CCM_M_0 0xC8A4 +#define MT9M114_VAR_CAM_AWB_CCM_M_1 0xC8A6 +#define MT9M114_VAR_CAM_AWB_CCM_M_2 0xC8A8 +#define MT9M114_VAR_CAM_AWB_CCM_M_3 0xC8AA +#define MT9M114_VAR_CAM_AWB_CCM_M_4 0xC8AC +#define MT9M114_VAR_CAM_AWB_CCM_M_5 0xC8AE +#define MT9M114_VAR_CAM_AWB_CCM_M_6 0xC8B0 +#define MT9M114_VAR_CAM_AWB_CCM_M_7 0xC8B2 +#define MT9M114_VAR_CAM_AWB_CCM_M_8 0xC8B4 +#define MT9M114_VAR_CAM_AWB_CCM_R_0 0xC8B6 +#define MT9M114_VAR_CAM_AWB_CCM_R_1 0xC8B8 +#define MT9M114_VAR_CAM_AWB_CCM_R_2 0xC8BA +#define MT9M114_VAR_CAM_AWB_CCM_R_3 0xC8BC +#define MT9M114_VAR_CAM_AWB_CCM_R_4 0xC8BE +#define MT9M114_VAR_CAM_AWB_CCM_R_5 0xC8C0 +#define MT9M114_VAR_CAM_AWB_CCM_R_6 0xC8C2 +#define MT9M114_VAR_CAM_AWB_CCM_R_7 0xC8C4 +#define MT9M114_VAR_CAM_AWB_CCM_R_8 0xC8C6 +#define MT9M114_VAR_CAM_AWB_CCM_L_RG_GAIN 0xC8C8 +#define MT9M114_VAR_CAM_AWB_CCM_L_BG_GAIN 0xC8CA +#define MT9M114_VAR_CAM_AWB_CCM_M_RG_GAIN 0xC8CC +#define MT9M114_VAR_CAM_AWB_CCM_M_BG_GAIN 0xC8CE +#define MT9M114_VAR_CAM_AWB_CCM_R_RG_GAIN 0xC8D0 +#define MT9M114_VAR_CAM_AWB_CCM_R_BG_GAIN 0xC8D2 +#define MT9M114_VAR_CAM_AWB_CCM_L_CTEMP 0xC8D4 +#define MT9M114_VAR_CAM_AWB_CCM_M_CTEMP 0xC8D6 +#define MT9M114_VAR_CAM_AWB_CCM_R_CTEMP 0xC8D8 +#define MT9M114_VAR_CAM_AWB_LL_CCM_0 0xC8DA +#define MT9M114_VAR_CAM_AWB_LL_CCM_1 0xC8DC +#define MT9M114_VAR_CAM_AWB_LL_CCM_2 0xC8DE +#define MT9M114_VAR_CAM_AWB_LL_CCM_3 0xC8E0 +#define MT9M114_VAR_CAM_AWB_LL_CCM_4 0xC8E2 +#define MT9M114_VAR_CAM_AWB_LL_CCM_5 0xC8E4 +#define MT9M114_VAR_CAM_AWB_LL_CCM_6 0xC8E6 +#define MT9M114_VAR_CAM_AWB_LL_CCM_7 0xC8E8 +#define MT9M114_VAR_CAM_AWB_LL_CCM_8 0xC8EA +#define MT9M114_VAR_CAM_AWB_COLOR_TEMPERATURE_MIN 0xC8EC +#define MT9M114_VAR_CAM_AWB_COLOR_TEMPERATURE_MAX 0xC8EE +#define MT9M114_VAR_CAM_AWB_COLOR_TEMPERATURE 0xC8F0 +#define MT9M114_VAR_CAM_AWB_AWB_XSCALE 0xC8F2 +#define MT9M114_VAR_CAM_AWB_AWB_YSCALE 0xC8F3 +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_0 0xC8F4 +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_1 0xC8F6 +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_2 0xC8F8 +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_3 0xC8FA +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_4 0xC8FC +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_5 0xC8FE +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_6 0xC900 +#define MT9M114_VAR_CAM_AWB_AWB_WEIGHTS_7 0xC902 +#define MT9M114_VAR_CAM_AWB_AWB_XSHIFT_PRE_ADJ 0xC904 +#define MT9M114_VAR_CAM_AWB_AWB_YSHIFT_PRE_ADJ 0xC906 +#define MT9M114_VAR_CAM_AWB_AWBMODE 0xC909 +#define MT9M114_VAR_CAM_AWB_TINTS_CTEMP_THRESHOLD 0xC90A +#define MT9M114_VAR_CAM_AWB_K_R_L 0xC90C +#define MT9M114_VAR_CAM_AWB_K_G_L 0xC90D +#define MT9M114_VAR_CAM_AWB_K_B_L 0xC90E +#define MT9M114_VAR_CAM_AWB_K_R_R 0xC90F +#define MT9M114_VAR_CAM_AWB_K_G_R 0xC910 +#define MT9M114_VAR_CAM_AWB_K_B_R 0xC911 +#define MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_XSTART 0xC914 +#define MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_YSTART 0xC916 +#define MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_XEND 0xC918 +#define MT9M114_VAR_CAM_STAT_AWB_CLIP_WINDOW_YEND 0xC91A +#define MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_XSTART 0xC91C +#define MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_YSTART 0xC91E +#define MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_XEND 0xC920 +#define MT9M114_VAR_CAM_STAT_AE_INITIAL_WINDOW_YEND 0xC922 +#define MT9M114_VAR_CAM_LL_LLMODE 0xC924 +#define MT9M114_VAR_CAM_LL_START_BRIGHTNESS 0xC926 +#define MT9M114_VAR_CAM_LL_STOP_BRIGHTNESS 0xC928 +#define MT9M114_VAR_CAM_LL_START_SATURATION 0xC92A +#define MT9M114_VAR_CAM_LL_END_SATURATION 0xC92B +#define MT9M114_VAR_CAM_LL_START_DESATURATION 0xC92C +#define MT9M114_VAR_CAM_LL_END_DESATURATION 0xC92D +#define MT9M114_VAR_CAM_LL_START_DEMOSAIC 0xC92E +#define MT9M114_VAR_CAM_LL_START_AP_GAIN 0xC92F +#define MT9M114_VAR_CAM_LL_START_AP_THRESH 0xC930 +#define MT9M114_VAR_CAM_LL_STOP_DEMOSAIC 0xC931 +#define MT9M114_VAR_CAM_LL_STOP_AP_GAIN 0xC932 +#define MT9M114_VAR_CAM_LL_STOP_AP_THRESH 0xC933 +#define MT9M114_VAR_CAM_LL_START_NR_RED 0xC934 +#define MT9M114_VAR_CAM_LL_START_NR_GREEN 0xC935 +#define MT9M114_VAR_CAM_LL_START_NR_BLUE 0xC936 +#define MT9M114_VAR_CAM_LL_START_NR_THRESH 0xC937 +#define MT9M114_VAR_CAM_LL_STOP_NR_RED 0xC938 +#define MT9M114_VAR_CAM_LL_STOP_NR_GREEN 0xC939 +#define MT9M114_VAR_CAM_LL_STOP_NR_BLUE 0xC93A +#define MT9M114_VAR_CAM_LL_STOP_NR_THRESH 0xC93B +#define MT9M114_VAR_CAM_LL_START_CONTRAST_BM 0xC93C +#define MT9M114_VAR_CAM_LL_STOP_CONTRAST_BM 0xC93E +#define MT9M114_VAR_CAM_LL_GAMMA 0xC940 +#define MT9M114_VAR_CAM_LL_START_CONTRAST_GRADIENT 0xC942 +#define MT9M114_VAR_CAM_LL_STOP_CONTRAST_GRADIENT 0xC943 +#define MT9M114_VAR_CAM_LL_START_CONTRAST_LUMA_PERCENTAGE 0xC944 +#define MT9M114_VAR_CAM_LL_STOP_CONTRAST_LUMA_PERCENTAGE 0xC945 +#define MT9M114_VAR_CAM_LL_START_GAIN_METRIC 0xC946 +#define MT9M114_VAR_CAM_LL_STOP_GAIN_METRIC 0xC948 +#define MT9M114_VAR_CAM_LL_START_FADE_TO_BLACK_LUMA 0xC94A +#define MT9M114_VAR_CAM_LL_STOP_FADE_TO_BLACK_LUMA 0xC94C +#define MT9M114_VAR_CAM_LL_CLUSTER_DC_TH_BM 0xC94E +#define MT9M114_VAR_CAM_LL_CLUSTER_DC_GATE_PERCENTAGE 0xC950 +#define MT9M114_VAR_CAM_LL_SUMMING_SENSITIVITY_FACTOR 0xC951 +#define MT9M114_VAR_CAM_LL_START_TARGET_LUMA_BM 0xC952 +#define MT9M114_VAR_CAM_LL_STOP_TARGET_LUMA_BM 0xC954 +#define MT9M114_VAR_CAM_LL_INV_BRIGHTNESS_METRIC 0xC956 +#define MT9M114_VAR_CAM_LL_GAIN_METRIC 0xC958 +#define MT9M114_VAR_CAM_SEQ_UV_COLOR_BOOST 0xC95A +#define MT9M114_VAR_CAM_PGA_PGA_CONTROL 0xC95E +#define MT9M114_VAR_CAM_PGA_L_CONFIG_COLOUR_TEMP 0xC960 +#define MT9M114_VAR_CAM_PGA_L_CONFIG_GREEN_RED_Q14 0xC962 +#define MT9M114_VAR_CAM_PGA_L_CONFIG_RED_Q14 0xC964 +#define MT9M114_VAR_CAM_PGA_L_CONFIG_GREEN_BLUE_Q14 0xC966 +#define MT9M114_VAR_CAM_PGA_L_CONFIG_BLUE_Q14 0xC968 +#define MT9M114_VAR_CAM_PGA_M_CONFIG_COLOUR_TEMP 0xC96A +#define MT9M114_VAR_CAM_PGA_M_CONFIG_GREEN_RED_Q14 0xC96C +#define MT9M114_VAR_CAM_PGA_M_CONFIG_RED_Q14 0xC96E +#define MT9M114_VAR_CAM_PGA_M_CONFIG_GREEN_BLUE_Q14 0xC970 +#define MT9M114_VAR_CAM_PGA_M_CONFIG_BLUE_Q14 0xC972 +#define MT9M114_VAR_CAM_PGA_R_CONFIG_COLOUR_TEMP 0xC974 +#define MT9M114_VAR_CAM_PGA_R_CONFIG_GREEN_RED_Q14 0xC976 +#define MT9M114_VAR_CAM_PGA_R_CONFIG_RED_Q14 0xC978 +#define MT9M114_VAR_CAM_PGA_R_CONFIG_GREEN_BLUE_Q14 0xC97A +#define MT9M114_VAR_CAM_PGA_R_CONFIG_BLUE_Q14 0xC97C +#define MT9M114_VAR_CAM_SYSCTL_PLL_ENABLE 0xC97E +#define MT9M114_VAR_CAM_SYSCTL_PLL_DIVIDER_M_N 0xC980 +#define MT9M114_VAR_CAM_SYSCTL_PLL_DIVIDER_P 0xC982 +#define MT9M114_VAR_CAM_PORT_OUTPUT_CONTROL 0xC984 +#define MT9M114_VAR_CAM_PORT_PORCH 0xC986 +#define MT9M114_VAR_CAM_PORT_MIPI_TIMING_T_HS_ZERO 0xC988 +#define MT9M114_VAR_CAM_PORT_MIPI_TIMING_T_HS_EXIT_HS_TRAIL 0xC98A +#define MT9M114_VAR_CAM_PORT_MIPI_TIMING_T_CLK_POST_CLK_PRE 0xC98C +#define MT9M114_VAR_CAM_PORT_MIPI_TIMING_T_CLK_TRAIL_CLK_ZERO 0xC98E +#define MT9M114_VAR_CAM_PORT_MIPI_TIMING_T_LPX 0xC990 +#define MT9M114_VAR_CAM_PORT_MIPI_TIMING_INIT_TIMING 0xC992 + +/* 10.UVC_Control variables */ +#define MT9M114_VAR_UVC_AE_MODE_CONTROL 0xCC00 +#define MT9M114_VAR_UVC_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0xCC01 +#define MT9M114_VAR_UVC_AE_PRIORITY_CONTROL 0xCC02 +#define MT9M114_VAR_UVC_POWER_LINE_FREQUENCY_CONTROL 0xCC03 +#define MT9M114_VAR_UVC_EXPOSURE_TIME_ABSOLUTE_CONTROL 0xCC04 +#define MT9M114_VAR_UVC_BACKLIGHT_COMPENSATION_CONTROL 0xCC08 +#define MT9M114_VAR_UVC_BRIGHTNESS_CONTROL 0xCC0A +#define MT9M114_VAR_UVC_CONTRAST_CONTROL 0xCC0C +#define MT9M114_VAR_UVC_GAIN_CONTROL 0xCC0E +#define MT9M114_VAR_UVC_HUE_CONTROL 0xCC10 +#define MT9M114_VAR_UVC_SATURATION_CONTROL 0xCC12 +#define MT9M114_VAR_UVC_SHARPNESS_CONTROL 0xCC14 +#define MT9M114_VAR_UVC_GAMMA_CONTROL 0xCC16 +#define MT9M114_VAR_UVC_WHITE_BALANCE_TEMPERATURE_CONTROL 0xCC18 +#define MT9M114_VAR_UVC_FRAME_INTERVAL_CONTROL 0xCC1C +#define MT9M114_VAR_UVC_MANUAL_EXPOSURE_CONFIGURATION 0xCC20 +#define MT9M114_VAR_UVC_FLICKER_AVOIDANCE_CONFIGURATION 0xCC21 +#define MT9M114_VAR_UVC_ALGO 0xCC22 +#define MT9M114_VAR_UVC_RESULT_STATUS 0xCC24 + +/* 11.SystemManager variables */ +#define MT9M114_VAR_SYSMGR_NEXT_STATE 0xDC00 +#define MT9M114_VAR_SYSMGR_CURRENT_STATE 0xDC01 +#define MT9M114_VAR_SYSMGR_CMD_STATUS 0xDC02 + +/* 12.PatchLoader variables */ +#define MT9M114_VAR_PATCHLDR_LOADER_ADDRESS 0xE000 +#define MT9M114_VAR_PATCHLDR_PATCH_ID 0xE002 +#define MT9M114_VAR_PATCHLDR_FIRMWARE_ID 0xE004 +#define MT9M114_VAR_PATCHLDR_APPLY_STATUS 0xE008 +#define MT9M114_VAR_PATCHLDR_NUM_PATCHES 0xE009 +#define MT9M114_VAR_PATCHLDR_PATCH_ID_0 0xE00A +#define MT9M114_VAR_PATCHLDR_PATCH_ID_1 0xE00C +#define MT9M114_VAR_PATCHLDR_PATCH_ID_2 0xE00E +#define MT9M114_VAR_PATCHLDR_PATCH_ID_3 0xE010 +#define MT9M114_VAR_PATCHLDR_PATCH_ID_4 0xE012 +#define MT9M114_VAR_PATCHLDR_PATCH_ID_5 0xE014 +#define MT9M114_VAR_PATCHLDR_PATCH_ID_6 0xE016 +#define MT9M114_VAR_PATCHLDR_PATCH_ID_7 0xE018 + +/* 13.Patch variables */ +#define MT9M114_VAR_PATCHVARS_DELTA_DK_CORRECTION_FACTOR 0xE400 + +/* 14.CommandHandler variables */ +#define MT9M114_VAR_CMD_HANDLER_WAIT_EVENT_ID 0xFC00 +#define MT9M114_VAR_CMD_HANDLER_NUM_EVENTS 0xFC02 + +/*! @brief MT9M114 command definitions. */ +#define MT9M114_COMMAND_APPLY_PATCH 0x0001 +#define MT9M114_COMMAND_SET_STATE 0x0002 +#define MT9M114_COMMAND_REFRESH 0x0004 +#define MT9M114_COMMAND_WAIT_FOR_EVENT 0x0008 +#define MT9M114_COMMAND_OK 0x8000 + +/*! @brief MT9M114 system state definitions. */ +#define MT9M114_SYS_STATE_ENTER_CONFIG_CHANGE 0x28 +#define MT9M114_SYS_STATE_STREAMING 0x31 +#define MT9M114_SYS_STATE_START_STREAMING 0x34 +#define MT9M114_SYS_STATE_ENTER_SUSPEND 0x40 +#define MT9M114_SYS_STATE_SUSPENDED 0x41 +#define MT9M114_SYS_STATE_ENTER_STANDBY 0x50 +#define MT9M114_SYS_STATE_STANDBY 0x52 +#define MT9M114_SYS_STATE_LEAVE_STANDBY 0x54 + +/*! @brief MT9M114 system set-state command retults. */ +#define MT9M114_SYS_STATE_SET_RESULT_ENOERR 0x00 /* command successful */ +#define MT9M114_SYS_STATE_SET_RESULTEINVAL 0x0C /* invalid configuration */ +#define MT9M114_SYS_STATE_SET_RESULTENOSPC 0x0D /* resource not available */ + +extern const struct img_sensor_driver_api mt9m114_api; + +#endif /* ZEPHYR_DRIVERS_IMAGE_SENSOR_MT9M114_H_ */ diff --git a/drivers/camera/nxp_csi/CMakeLists.txt b/drivers/camera/nxp_csi/CMakeLists.txt new file mode 100644 index 000000000000..abcfaf1b2aba --- /dev/null +++ b/drivers/camera/nxp_csi/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_NXP_MCUX_CSI nxp_mcux_csi.c) diff --git a/drivers/camera/nxp_csi/Kconfig b/drivers/camera/nxp_csi/Kconfig new file mode 100644 index 000000000000..e8ca3893209b --- /dev/null +++ b/drivers/camera/nxp_csi/Kconfig @@ -0,0 +1,8 @@ +# +# Copyright (c) 2019, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +if NXP_MCUX_CSI +endif diff --git a/drivers/camera/nxp_csi/nxp_mcux_csi.c b/drivers/camera/nxp_csi/nxp_mcux_csi.c new file mode 100644 index 000000000000..a8eea73d094b --- /dev/null +++ b/drivers/camera/nxp_csi/nxp_mcux_csi.c @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2019, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "fsl_common.h" +#include +#include + +#define LOG_LEVEL LOG_LEVEL_ERR +#include +LOG_MODULE_REGISTER(NXP_MCUX_CSI); + +#define NXP_CSI_DBG + +#undef LOG_ERR +#define LOG_ERR printk + +#ifdef NXP_CSI_DBG +#undef LOG_INF +#define LOG_INF printk +#endif + +#define CSI_CSICR1_INT_EN_MASK 0xFFFF0000U +#define CSI_CSICR3_INT_EN_MASK 0x000000FFU +#define CSI_CSICR18_INT_EN_MASK 0x0000FF00U + +#define CSI_FB_DEFAULT_WIDTH 480 +#define CSI_FB_DEFAULT_HEIGHT 272 + +#define CSI_FB_DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_RGB_565 + +#define CSI_FB_MAX_NUM 8 + +struct priv_csi_config { + CSI_Type *base; + int irq_num; + u32_t polarity; + bool sensor_vsync; + /* In CCIR656 progressive mode, + * set true to use external VSYNC signal, set false + * to use internal VSYNC signal decoded from SOF. + */ +}; + +enum priv_csi_polarity_flags { + CSI_HSYNC_LOW = 0U, + /* HSYNC is active low. */ + CSI_HSYNC_HIGH = CSI_CSICR1_HSYNC_POL_MASK, + /* HSYNC is active high. */ + CSI_RISING_LATCH = CSI_CSICR1_REDGE_MASK, + /* Pixel data latched at rising edge of pixel clock. */ + CSI_FALLING_LATCH = 0U, + /* Pixel data latched at falling edge of pixel clock. */ + CSI_VSYNC_HIGH = 0U, + /* VSYNC is active high. */ + CSI_VSYNC_LOW = CSI_CSICR1_SOF_POL_MASK, + /* VSYNC is active low. */ +}; + +enum priv_csi_fifo { + CSI_RXFIFO = (1U << 0U), + /* RXFIFO. */ + CSI_STATFIFO = (1U << 1U), + /* STAT FIFO. */ + CSI_ALLFIFO = (CSI_RXFIFO | CSI_STATFIFO) +}; + +enum mcux_csi_status { + MCUX_CSI_INIT, + MCUX_CSI_POWER, + MCUX_CSI_READY, + MCUX_CSI_RUNNING, + MCUX_CSI_PAUSE +}; + +struct mcux_csi_fb { + void *fb[CSI_FB_MAX_NUM]; + u8_t fb_from[CSI_FB_MAX_NUM]; + struct k_mutex sw_hmutex; + struct k_mutex sw_tmutex; + u8_t sw_head; + u8_t sw_tail; + u8_t hw_head; + u8_t hw_tail; +}; + +struct mcux_csi_priv { + struct mcux_csi_fb csi_fb; + struct priv_csi_config hw_cfg; + struct device *clk_dev; + clock_control_subsys_t clock_sys; + u32_t mclk; + enum mcux_csi_status status; +}; + +static void mcux_csi_config_irq(struct camera_driver_data *data); + +static inline void csi_irq_configure(CSI_Type *base, u32_t mask) +{ + base->CSICR1 |= (mask & CSI_CSICR1_INT_EN_MASK); + base->CSICR3 |= (mask & CSI_CSICR3_INT_EN_MASK); + base->CSICR18 |= ((mask & CSI_CSICR18_INT_EN_MASK) >> 6U); +} + +static inline void csi_hw_fifo_dma_enable(CSI_Type *base, + enum priv_csi_fifo fifo, bool enable) +{ + u32_t cr3 = 0U; + + if ((u32_t)fifo & (u32_t)CSI_RXFIFO) { + cr3 |= CSI_CSICR3_DMA_REQ_EN_RFF_MASK; + } + + if ((u32_t)fifo & (u32_t)CSI_STATFIFO) { + cr3 |= CSI_CSICR3_DMA_REQ_EN_SFF_MASK; + } + + if (enable) { + base->CSICR3 |= cr3; + } else { + base->CSICR3 &= ~cr3; + } +} + +static int csi_start(struct camera_driver_data *data) +{ + struct mcux_csi_priv *priv = camera_data_priv(data); + u32_t cr3 = 0U; + CSI_Type *base = priv->hw_cfg.base; + + base->CSICR18 = + (base->CSICR18 & ~CSI_CSICR18_MASK_OPTION_MASK) | + CSI_CSICR18_MASK_OPTION(3) | + CSI_CSICR18_BASEADDR_SWITCH_SEL_MASK | + CSI_CSICR18_BASEADDR_SWITCH_EN_MASK; + + if (data->mode == CAMERA_CAPTURE_MODE) { + base->CSIDMASA_FB1 = (u32_t)priv->csi_fb.fb[0]; + base->CSIDMASA_FB2 = (u32_t)priv->csi_fb.fb[0]; + } else { + base->CSIDMASA_FB1 = (u32_t)priv->csi_fb.fb[0]; + priv->csi_fb.hw_tail++; + if (priv->csi_fb.hw_tail != + priv->csi_fb.sw_tail) { + base->CSIDMASA_FB2 = (u32_t)priv->csi_fb.fb[1]; + priv->csi_fb.hw_tail++; + } else { + base->CSIDMASA_FB2 = (u32_t)priv->csi_fb.fb[0]; + } + } + + /* After reflash DMA, the CSI saves frame to frame buffer 0. */ + cr3 |= CSI_CSICR3_DMA_REFLASH_RFF_MASK; + base->CSICR3 |= cr3; + while (base->CSICR3 & cr3) { + ; + } + + /*Enable isr*/ + if (data->mode == CAMERA_CAPTURE_MODE) { + csi_irq_configure(base, CSI_CSICR1_FB1_DMA_DONE_INTEN_MASK); + } else { + csi_irq_configure(base, CSI_CSICR1_FB1_DMA_DONE_INTEN_MASK | + CSI_CSICR1_FB2_DMA_DONE_INTEN_MASK); + } + + irq_enable(priv->hw_cfg.irq_num); + + /*Start capture*/ + csi_hw_fifo_dma_enable(base, CSI_RXFIFO, true); + + base->CSICR18 |= CSI_CSICR18_CSI_ENABLE_MASK; + + return 0; +} + +static inline void csi_hw_stop(CSI_Type *base) +{ + base->CSICR18 &= ~CSI_CSICR18_CSI_ENABLE_MASK; + csi_hw_fifo_dma_enable(base, CSI_RXFIFO, false); +} + +static int mcux_csi_start(struct device *cam_dev, + enum camera_mode mode, void **bufs, + u8_t buf_num, + camera_capture_cb cb) +{ + struct camera_driver_data *data = cam_dev->driver_data; + struct mcux_csi_priv *priv = camera_data_priv(data); + u8_t i; + int ret; + + if (buf_num < 1 || buf_num > (CSI_FB_MAX_NUM - 1)) { + return -EINVAL; + } + + for (i = 0; i < buf_num; i++) { + priv->csi_fb.fb[i] = bufs[i]; + } + priv->csi_fb.sw_head = 0; + priv->csi_fb.sw_tail = buf_num; + priv->csi_fb.hw_head = 0; + priv->csi_fb.hw_tail = 0; + data->mode = mode; + data->customer_cb = cb; + + ret = csi_start(data); + if (!ret) { + priv->status = MCUX_CSI_RUNNING; + } + + return ret; +} + +static int mcux_csi_resume(struct device *cam_dev) +{ + struct camera_driver_data *data = cam_dev->driver_data; + struct mcux_csi_priv *priv = camera_data_priv(data); + int ret; + + ret = csi_start(data); + if (!ret) { + priv->status = MCUX_CSI_RUNNING; + } + + return ret; +} + +static int mcux_csi_acquire_fb(struct device *dev, + void **fb, s32_t timeout) +{ + struct camera_driver_data *data = dev->driver_data; + struct mcux_csi_priv *priv = camera_data_priv(data); + + if (data->mode == CAMERA_CAPTURE_MODE) { + *fb = priv->csi_fb.fb[priv->csi_fb.sw_head]; + return 0; + } + + k_mutex_lock(&priv->csi_fb.sw_hmutex, timeout); + +acquire_again: + if (priv->csi_fb.sw_head == + priv->csi_fb.hw_head) { + if (timeout == K_NO_WAIT) { + *fb = 0; + k_mutex_unlock(&priv->csi_fb.sw_hmutex); + return -ENOBUFS; + } + + k_sleep(1); + timeout--; + goto acquire_again; + } + + *fb = priv->csi_fb.fb[priv->csi_fb.sw_head]; + + if (priv->csi_fb.sw_head == + (CSI_FB_MAX_NUM - 1)) { + priv->csi_fb.sw_head = 0; + } else { + priv->csi_fb.sw_head++; + } + + k_mutex_unlock(&priv->csi_fb.sw_hmutex); + + return 0; +} + +static int mcux_csi_release_fb(struct device *dev, void *fb) +{ + struct camera_driver_data *data = dev->driver_data; + struct mcux_csi_priv *priv = camera_data_priv(data); + + k_mutex_lock(&priv->csi_fb.sw_tmutex, K_FOREVER); + if ((priv->csi_fb.sw_tail + 1) == + priv->csi_fb.sw_head || + (priv->csi_fb.sw_tail == + (CSI_FB_MAX_NUM - 1) && + priv->csi_fb.sw_head == 0)) { + k_mutex_unlock(&priv->csi_fb.sw_tmutex); + return -ENOSPC; + } + + priv->csi_fb.fb[priv->csi_fb.sw_tail] = fb; + + if (priv->csi_fb.sw_tail == + (CSI_FB_MAX_NUM - 1)) { + priv->csi_fb.sw_tail = 0; + } else { + priv->csi_fb.sw_tail++; + } + + k_mutex_unlock(&priv->csi_fb.sw_tmutex); + + if (priv->status == MCUX_CSI_PAUSE) { + mcux_csi_resume(dev); + } + + return 0; +} + +static void mcux_csi_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + struct camera_driver_data *data = dev->driver_data; + struct mcux_csi_priv *priv = camera_data_priv(data); + CSI_Type *base = priv->hw_cfg.base; + u32_t csisr = base->CSISR; + void *fb; + + /* Clear the error flags. */ + base->CSISR = csisr; + + if (data->mode == CAMERA_PREVIEW_MODE) { + if (csisr & CSI_CSISR_DMA_TSF_DONE_FB1_MASK) { + fb = (void *)base->CSIDMASA_FB1; + priv->csi_fb.fb_from[priv->csi_fb.hw_head] = 1; + if (priv->csi_fb.hw_head < + (CSI_FB_MAX_NUM - 1)) { + priv->csi_fb.hw_head++; + } else { + priv->csi_fb.hw_head = 0; + } + + base->CSIDMASA_FB1 = + (u32_t)priv->csi_fb.fb[priv->csi_fb.hw_tail]; + + if (priv->csi_fb.hw_tail != + priv->csi_fb.sw_tail) { + if (priv->csi_fb.hw_tail < + (CSI_FB_MAX_NUM - 1)) { + priv->csi_fb.hw_tail++; + } else { + priv->csi_fb.hw_tail = 0; + } + } else { + csi_hw_stop(base); + priv->status = MCUX_CSI_PAUSE; + LOG_ERR("FB1 stop\r\n"); + } + if (data->customer_cb) { + data->customer_cb(fb, + data->fb_attr.width, + data->fb_attr.height, + data->fb_attr.bpp); + } + } + + if (csisr & CSI_CSISR_DMA_TSF_DONE_FB2_MASK) { + fb = (void *)base->CSIDMASA_FB2; + priv->csi_fb.fb_from[priv->csi_fb.hw_head] = 2; + if (priv->csi_fb.hw_head < + (CSI_FB_MAX_NUM - 1)) { + priv->csi_fb.hw_head++; + } else { + priv->csi_fb.hw_head = 0; + } + + base->CSIDMASA_FB2 = + (u32_t)priv->csi_fb.fb[priv->csi_fb.hw_tail]; + + if (priv->csi_fb.hw_tail != + priv->csi_fb.sw_tail) { + if (priv->csi_fb.hw_tail < + (CSI_FB_MAX_NUM - 1)) { + priv->csi_fb.hw_tail++; + } else { + priv->csi_fb.hw_tail = 0; + } + } else { + csi_hw_stop(base); + priv->status = MCUX_CSI_PAUSE; + LOG_ERR("FB2 stop\r\n"); + } + if (data->customer_cb) { + data->customer_cb(fb, + data->fb_attr.width, + data->fb_attr.height, + data->fb_attr.bpp); + } + } + + return; + } + + fb = (void *)base->CSIDMASA_FB1; + /*Stop CSI*/ + base->CSICR18 &= ~CSI_CSICR18_CSI_ENABLE_MASK; + base->CSICR3 &= ~CSI_CSICR3_DMA_REQ_EN_RFF_MASK; + + priv->status = MCUX_CSI_PAUSE; + + if (data->customer_cb) { + data->customer_cb(fb, + data->fb_attr.width, + data->fb_attr.height, + data->fb_attr.bpp); + } +} + +static void csi_hw_clear_fifo(CSI_Type *base, enum priv_csi_fifo fifo) +{ + u32_t cr1; + u32_t mask = 0U; + + /* The FIFO could only be cleared when CSICR1[FCC] = 0, + * so first clear the FCC. + */ + cr1 = base->CSICR1; + base->CSICR1 = (cr1 & ~CSI_CSICR1_FCC_MASK); + + if ((u32_t)fifo & (u32_t)CSI_RXFIFO) { + mask |= CSI_CSICR1_CLR_RXFIFO_MASK; + } + + if ((u32_t)fifo & (u32_t)CSI_STATFIFO) { + mask |= CSI_CSICR1_CLR_STATFIFO_MASK; + } + + base->CSICR1 = (cr1 & ~CSI_CSICR1_FCC_MASK) | mask; + + /* Wait clear completed. */ + while (base->CSICR1 & mask) { + ; + } + + /* Recover the FCC. */ + base->CSICR1 = cr1; +} + +static void csi_hw_reflash_fifo(CSI_Type *base, enum priv_csi_fifo fifo) +{ + u32_t cr3 = 0U; + + if ((u32_t)fifo & (u32_t)CSI_RXFIFO) { + cr3 |= CSI_CSICR3_DMA_REFLASH_RFF_MASK; + } + + if ((u32_t)fifo & (u32_t)CSI_STATFIFO) { + cr3 |= CSI_CSICR3_DMA_REFLASH_SFF_MASK; + } + + base->CSICR3 |= cr3; + + /* Wait clear completed. */ + while (base->CSICR3 & cr3) { + ; + } +} + +static void csi_hw_reset(CSI_Type *base) +{ + u32_t csisr; + + /* Disable transfer first. */ + csi_hw_stop(base); + + /* Disable DMA request. */ + base->CSICR3 = 0U; + + /* Reset the fame count. */ + base->CSICR3 |= CSI_CSICR3_FRMCNT_RST_MASK; + while (base->CSICR3 & CSI_CSICR3_FRMCNT_RST_MASK) { + ; + } + + /* Clear the RX FIFO. */ + csi_hw_clear_fifo(base, CSI_ALLFIFO); + + /* Reflash DMA. */ + csi_hw_reflash_fifo(base, CSI_ALLFIFO); + + /* Clear the status. */ + csisr = base->CSISR; + base->CSISR = csisr; + + /* Set the control registers to default value. */ + base->CSICR1 = CSI_CSICR1_HSYNC_POL_MASK | CSI_CSICR1_EXT_VSYNC_MASK; + base->CSICR2 = 0U; + base->CSICR3 = 0U; + + base->CSICR18 = CSI_CSICR18_AHB_HPROT(0x0DU); + base->CSIFBUF_PARA = 0U; + base->CSIIMAG_PARA = 0U; +} + +static int csi_hw_data_config(struct camera_driver_data *data) +{ + struct mcux_csi_priv *priv = camera_data_priv(data); + u32_t reg; + u32_t width_bytes; + CSI_Type *base = priv->hw_cfg.base; + + width_bytes = data->fb_attr.width * + data->fb_attr.bpp; + + csi_hw_reset(base); + + /*!< HSYNC, VSYNC, and PIXCLK signals are used. */ + reg = ((u32_t)CSI_CSICR1_GCLK_MODE(1U)) | + priv->hw_cfg.polarity | + CSI_CSICR1_FCC_MASK; + + if (priv->hw_cfg.sensor_vsync) { + reg |= CSI_CSICR1_EXT_VSYNC_MASK; + } + + base->CSICR1 = reg; + + /* Image parameter. */ + base->CSIIMAG_PARA = + ((u32_t)(width_bytes) << + CSI_CSIIMAG_PARA_IMAGE_WIDTH_SHIFT) | + ((u32_t)data->fb_attr.height << + CSI_CSIIMAG_PARA_IMAGE_HEIGHT_SHIFT); + + /* The CSI frame buffer bus is 8-byte width. */ + base->CSIFBUF_PARA = 0; + + /* Enable auto ECC. */ + base->CSICR3 |= CSI_CSICR3_ECC_AUTO_EN_MASK; + + if (!(width_bytes % (8 * 16))) { + base->CSICR2 = CSI_CSICR2_DMA_BURST_TYPE_RFF(3U); + base->CSICR3 = + (base->CSICR3 & ~CSI_CSICR3_RxFF_LEVEL_MASK) | + ((2U << CSI_CSICR3_RxFF_LEVEL_SHIFT)); + } else if (!(width_bytes % (8 * 8))) { + base->CSICR2 = CSI_CSICR2_DMA_BURST_TYPE_RFF(2U); + base->CSICR3 = + (base->CSICR3 & ~CSI_CSICR3_RxFF_LEVEL_MASK) | + ((1U << CSI_CSICR3_RxFF_LEVEL_SHIFT)); + } else { + base->CSICR2 = CSI_CSICR2_DMA_BURST_TYPE_RFF(1U); + base->CSICR3 = + (base->CSICR3 & ~CSI_CSICR3_RxFF_LEVEL_MASK) | + ((0U << CSI_CSICR3_RxFF_LEVEL_SHIFT)); + } + + /*Reflash DMA*/ + base->CSICR3 |= CSI_CSICR3_DMA_REFLASH_RFF_MASK; + + /* Wait clear completed. */ + while (base->CSICR3 & CSI_CSICR3_DMA_REFLASH_RFF_MASK) { + ; + } + + return 0; +} + +static int mcux_csi_sensor_cfg(struct camera_driver_data *data) +{ + struct device *img_dev = data->sensor_dev; + int ret; + + ret = img_sensor_set_framesize(img_dev, + data->fb_attr.width, + data->fb_attr.height); + if (ret) { + return ret; + } + + ret = img_sensor_set_pixformat(img_dev, + data->fb_attr.pixformat); + if (ret) { + return ret; + } + + ret = img_sensor_configure(img_dev); + + return ret; +} + +static int mcux_csi_config(struct device *cam_dev, + struct camera_fb_cfg *fb_cfg) +{ + struct camera_driver_data *data = cam_dev->driver_data; + struct mcux_csi_priv *priv = camera_data_priv(data); + int ret; + + if (priv->status != MCUX_CSI_POWER) { + LOG_ERR("CSI configuration on the fly not implemented\r\n"); + + return -EACCES; + } + + if (fb_cfg->cfg_mode == CAMERA_USER_CFG) { + if (!(fb_cfg->fb_attr.pixformat & + data->cap.pixformat_support)) { + LOG_ERR("CSI pixel format 0x%08x not supported!\r\n", + fb_cfg->fb_attr.pixformat); + + return -ENOTSUP; + } + + if (fb_cfg->fb_attr.width > + data->cap.width_max || + fb_cfg->fb_attr.height > + data->cap.height_max) { + LOG_ERR("CSI frame size exceeds!\r\n"); + + return -ENOTSUP; + } + + if (fb_cfg->fb_attr.pixformat != + PIXEL_FORMAT_RGB_565) { + LOG_ERR("CSI other than RGB565 not implemented\r\n"); + + return -ENOTSUP; + } + } + + camera_dev_configure(cam_dev, fb_cfg); + + ret = mcux_csi_sensor_cfg(data); + if (ret) { + return ret; + } + + ret = csi_hw_data_config(data); + if (ret) { + return ret; + } + + mcux_csi_config_irq(data); + + return 0; +} + +static int mcux_csi_power(struct device *cam_dev, + bool power) +{ + struct camera_driver_data *data = cam_dev->driver_data; + struct mcux_csi_priv *priv = camera_data_priv(data); + struct device *img_dev = data->sensor_dev; + int ret; + struct img_sensor_capability sensor_cap; + + if (!img_dev) { + LOG_ERR("CSI power, but CMOS sensor Not present!\r\n"); + + return -ENODEV; + } + + if (power) { + if (priv->status != MCUX_CSI_INIT) { + return 0; + } + + CLOCK_EnableClock(kCLOCK_Csi); + imxrt_csi_mclk_enable(true); + k_sleep(1); + + csi_hw_reset(priv->hw_cfg.base); + + ret = z_impl_img_sensor_reset(img_dev); + if (ret) { + LOG_ERR("CMOS sensor reset failed with error: %d\r\n", + ret); + + return ret; + } + + z_impl_img_sensor_get_cap(img_dev, &sensor_cap); + if (ret) { + LOG_ERR("CMOS sensor get capability failed" + " with error: %d\r\n", + ret); + + return ret; + } + + data->cap.pixformat_support = + data->cap.pixformat_support & + sensor_cap.pixformat_support; + data->cap.width_max = + sensor_cap.width_max; + data->cap.height_max = + sensor_cap.height_max; + + priv->status = MCUX_CSI_POWER; + + return 0; + } + + CLOCK_DisableClock(kCLOCK_Csi); + imxrt_csi_mclk_enable(false); + + priv->status = MCUX_CSI_INIT; + + return 0; +} + +static int mcux_csi_reset(struct device *cam_dev) +{ + mcux_csi_power(cam_dev, false); + + k_sleep(1); + mcux_csi_power(cam_dev, true); + + return 0; +} + +static const struct camera_driver_api mcux_camera_api = { + .camera_power_cb = mcux_csi_power, + .camera_reset_cb = mcux_csi_reset, + .camera_get_cap_cb = camera_dev_get_cap, + .camera_configure_cb = mcux_csi_config, + .camera_start_cb = mcux_csi_start, + .camera_acquire_fb_cb = mcux_csi_acquire_fb, + .camera_release_fb_cb = mcux_csi_release_fb, +}; + +static int mcux_csi_init(struct device *cam_dev) +{ + struct camera_driver_data *data; + struct mcux_csi_priv *priv; + struct device *img_dev; + enum camera_id id; + +#ifdef DT_INST_0_NXP_IMX_CSI_LABEL +#if (DT_INST_0_NXP_IMX_CSI_LABEL == CAMERA_PRIMARY_LABEL) + id = CAMERA_PRIMARY_ID; +#elif (DT_INST_0_NXP_IMX_CSI_LABEL == CAMERA_SECONDARY_LABEL) + id = CAMERA_SECONDARY_ID; +#else +#warning CSI LABEL should be "primary" or "secondary". + id = CAMERA_PRIMARY_ID; +#endif +#else + id = CAMERA_PRIMARY_ID; +#endif + + data = camera_drv_data_alloc(sizeof(struct mcux_csi_priv), id, true); + cam_dev->driver_data = data; + priv = camera_data_priv(data); + + priv->hw_cfg.base = (CSI_Type *)DT_INST_0_NXP_IMX_CSI_BASE_ADDRESS; + priv->hw_cfg.irq_num = (int)DT_INST_0_NXP_IMX_CSI_IRQ_0; + priv->hw_cfg.polarity = (CSI_HSYNC_HIGH | + CSI_RISING_LATCH); + priv->hw_cfg.sensor_vsync = true; + + k_mutex_init(&priv->csi_fb.sw_hmutex); + k_mutex_init(&priv->csi_fb.sw_tmutex); + + data->cap.fb_alignment = 64; + data->cap.pixformat_support = + PIXEL_FORMAT_RGB_565 | + PIXEL_FORMAT_RGB_888; + + data->fb_attr.width = CSI_FB_DEFAULT_WIDTH; + data->fb_attr.height = CSI_FB_DEFAULT_HEIGHT; + data->fb_attr.pixformat = CSI_FB_DEFAULT_PIXEL_FORMAT; + if (data->fb_attr.pixformat == + PIXEL_FORMAT_RGB_888) { + data->fb_attr.bpp = 3; + } else if (data->fb_attr.pixformat == + PIXEL_FORMAT_ARGB_8888) { + data->fb_attr.bpp = 4; + } else if (data->fb_attr.pixformat == + PIXEL_FORMAT_RGB_565) { + data->fb_attr.bpp = 2; + } else { + LOG_ERR("CSI does not support this pixel format %d\r\n", + data->fb_attr.pixformat); + return -EINVAL; + } + priv->status = MCUX_CSI_INIT; + + priv->clk_dev = device_get_binding( + DT_INST_0_NXP_IMX_CSI_CLOCK_CONTROLLER); + priv->clock_sys = + (clock_control_subsys_t)DT_INST_0_NXP_IMX_CSI_CLOCK_NAME; + + CLOCK_SetDiv(kCLOCK_CsiDiv, 0); + CLOCK_SetMux(kCLOCK_CsiMux, 0); + CLOCK_EnableClock(kCLOCK_Csi); + imxrt_csi_mclk_enable(true); + + if (clock_control_get_rate(priv->clk_dev, priv->clock_sys, + &priv->mclk)) { + return -EINVAL; + } + + img_dev = img_sensor_scan(data->id); + if (!img_dev) { + LOG_ERR("CSI init No CMOS sensor present!\r\n"); + + return -ENODEV; + } + + data->sensor_dev = img_dev; + + /*Power off for power saving + * Until user powers on. + */ + CLOCK_DisableClock(kCLOCK_Csi); + imxrt_csi_mclk_enable(false); + + return camera_dev_register(cam_dev); +} + +DEVICE_AND_API_INIT(mcux_csi, "MCUX_CSI", + &mcux_csi_init, + 0, 0, + POST_KERNEL, CONFIG_CAMERA_INIT_PRIO, + &mcux_camera_api); + +static void mcux_csi_config_irq(struct camera_driver_data *data) +{ + IRQ_CONNECT(DT_INST_0_NXP_IMX_CSI_IRQ_0, + 0, mcux_csi_isr, DEVICE_GET(mcux_csi), 0); + + irq_enable(DT_INST_0_NXP_IMX_CSI_IRQ_0); +} diff --git a/drivers/clock_control/clock_control_mcux_ccm.c b/drivers/clock_control/clock_control_mcux_ccm.c index 04ed92552a7e..3680e8a5001c 100644 --- a/drivers/clock_control/clock_control_mcux_ccm.c +++ b/drivers/clock_control/clock_control_mcux_ccm.c @@ -32,6 +32,15 @@ static int mcux_ccm_off(struct device *dev, return 0; } +#ifdef CONFIG_NXP_MCUX_CSI +enum mcux_csi_clk_sel { + CSI_CLK_SEL_24M = 0, + CSI_CLK_SEL_PLL2_PFD2 = 1, + CSI_CLK_SEL_120M = 2, + CSI_CLK_SEL_PLL3_PFD1 = 3, +}; +#endif + static int mcux_ccm_get_subsys_rate(struct device *dev, clock_control_subsys_t sub_system, u32_t *rate) @@ -87,6 +96,32 @@ static int mcux_ccm_get_subsys_rate(struct device *dev, (CLOCK_GetDiv(kCLOCK_Usdhc2Div) + 1U); break; #endif + +#ifdef CONFIG_NXP_MCUX_CSI + case IMX_CCM_CSI_CLK: + { + enum mcux_csi_clk_sel clk_sel = + (enum mcux_csi_clk_sel) + CLOCK_GetMux(kCLOCK_CsiMux); + + if (clk_sel == CSI_CLK_SEL_24M) { + *rate = (24 * 1024 * 1024) / + (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1U); + } else if (clk_sel == CSI_CLK_SEL_PLL2_PFD2) { + *rate = CLOCK_GetSysPfdFreq(kCLOCK_Pfd2) / + (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1U); + } else if (clk_sel == CSI_CLK_SEL_120M) { + *rate = (120 * 1024 * 1024) / + (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1U); + } else if (clk_sel == CSI_CLK_SEL_PLL3_PFD1) { + *rate = CLOCK_GetSysPfdFreq(kCLOCK_Pfd1) / + (CLOCK_GetDiv(kCLOCK_CsiDiv) + 1U); + } else { + return -EINVAL; + } + } + break; +#endif } return 0; diff --git a/dts/arm/nxp/nxp_rt.dtsi b/dts/arm/nxp/nxp_rt.dtsi index 655c7b4cc4c1..1f6cc7fe99cd 100644 --- a/dts/arm/nxp/nxp_rt.dtsi +++ b/dts/arm/nxp/nxp_rt.dtsi @@ -388,6 +388,14 @@ clocks = <&ccm IMX_CCM_USDHC2_CLK 0 0>; label = "USDHC_2"; }; + + csi1: csi@402bc000 { + compatible = "nxp,imx-csi"; + reg = <0x402bc000 0x4000>; + status = "disabled"; + interrupts = <43 0>; + clocks = <&ccm IMX_CCM_CSI_CLK 0 0>; + }; }; }; diff --git a/dts/bindings/camera/camera.yaml b/dts/bindings/camera/camera.yaml new file mode 100644 index 000000000000..6ec8c72684f7 --- /dev/null +++ b/dts/bindings/camera/camera.yaml @@ -0,0 +1,31 @@ +# +# Copyright (c) 2019, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: camera module +version: 0.1 + +description: > + This binding specifies the camera module. + If there are two cameras present, one is primary + camera and the other is secondary camera. + +inherits: + !include base.yaml + +properties: + compatible: + category: required + + clocks: + type: array + category: optional + description: Clock gate information + generation: define + + label: + category: optional + description: Primary camera("primary") or Secondary camera("secondary"). +... diff --git a/dts/bindings/camera/image-sensor.yaml b/dts/bindings/camera/image-sensor.yaml new file mode 100644 index 000000000000..b5036adb8ccb --- /dev/null +++ b/dts/bindings/camera/image-sensor.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2019, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: CMOS Image Sensor +version: 0.1 + +description: > + This is a representation of the CMOS Sensor nodes. + +inherits: + !include i2c-device.yaml + +properties: + compatible: + constraint: "zephyr,image-sensor" + + pwr-gpios: + type: compound + category: optional + description: power control + generation: define +... diff --git a/dts/bindings/camera/nxp,imx-csi.yaml b/dts/bindings/camera/nxp,imx-csi.yaml new file mode 100644 index 000000000000..419d56f9b90b --- /dev/null +++ b/dts/bindings/camera/nxp,imx-csi.yaml @@ -0,0 +1,22 @@ +# +# Copyright (c) 2019, NXP +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: NXP CMOS SENSOR INTERFACE +version: 0.1 + +description: > + This binding gives a base representation of the NXP i.MX RT CSI nodes. + +inherits: + !include camera.yaml + +properties: + compatible: + constraint: "nxp,imx-csi" + + clocks: + category: required +... diff --git a/ext/hal/nxp/mcux/Kconfig b/ext/hal/nxp/mcux/Kconfig index 272cbb45dffb..848226c43317 100644 --- a/ext/hal/nxp/mcux/Kconfig +++ b/ext/hal/nxp/mcux/Kconfig @@ -144,4 +144,9 @@ config HAS_MCUX_WDOG32 help Set if the watchdog (WDOG32) module is present in the SoC. +config HAS_MCUX_CSI + bool + help + Set if the cmos sensor interface is present in the SoC. + endif # HAS_MCUX diff --git a/include/drivers/camera.h b/include/drivers/camera.h new file mode 100644 index 000000000000..65d6230c7406 --- /dev/null +++ b/include/drivers/camera.h @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2019, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public API for camera applications + */ + +#ifndef ZEPHYR_INCLUDE_CAMERA_H_ +#define ZEPHYR_INCLUDE_CAMERA_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Camera work flow: + * camera_get_primary/secondary->camera_power->camera_get_cap-> + * | + * <--camera_acquire_fb<---camera_start<-camera_configure<--| + * | | + * | | + * |-->camera_release_fb-^ + * + */ + +/** + * @enum camera_mode + * @brief Enumeration with camera running mode + * + */ +enum camera_mode { + /* If selected, only one frame is captured then + * camera stream is stopped. + */ + CAMERA_CAPTURE_MODE, + /* If selected, series of frames are captured if + * there are buffers available. + */ + CAMERA_PREVIEW_MODE +}; + +/** + * @typedef camera_capture_cb + * @brief Callback API when one frame is received + * This callback is supposed to be running in + * interrupt context. + * @param fb Pointer to frame buffer + * @param w width of the frame received. + * @param h height of the frame received. + * @param bpp bits per pixel of the frame received. + */ +typedef void (*camera_capture_cb)(void *fb, int w, int h, int bpp); + +/** + * @struct camera_fb_attr + * @brief Structure to describe attribution of + * camera frame buffer + * + */ +struct camera_fb_attr { + u32_t width; + u32_t height; + u8_t bpp; + /*Align the pixel format with display device.*/ + enum display_pixel_format pixformat; +}; + +/** + * @enum camera_cfg_mode + * @brief Enumeration to configure camera + * + */ +enum camera_cfg_mode { + /* If selected, configure camera with + * default camera settings + */ + CAMERA_DEFAULT_CFG, + /* If selected, configure camera with + * camera settings defined by user + */ + CAMERA_USER_CFG +}; + +/** + * @struct camera_fb_cfg + * @brief Structure to configure camera + * Parameter of camera_configure API. + */ +struct camera_fb_cfg { + enum camera_cfg_mode cfg_mode; + struct camera_fb_attr fb_attr; +}; + +/** + * @struct camera_capability + * @brief Structure to describe capabilities of camera + */ +struct camera_capability { + /*Alignment size of point to frame buffer, + * depends on camera DMA + */ + u8_t fb_alignment; + + /*pixel format support by camera, + * partly depends on image sensor connected + */ + u32_t pixformat_support; + + /* Resolution support by camera, + * for most case, resolution support + * depends on image sensor connected. + */ + u16_t width_max; + u16_t height_max; +}; + +/** + * @struct camera_driver_api + * @brief Structure to describe API for user to access camera + */ +struct camera_driver_api { + int (*camera_power_cb)(struct device *cam_dev, + bool power); + int (*camera_reset_cb)(struct device *cam_dev); + int (*camera_get_cap_cb)(struct device *cam_dev, + struct camera_capability *cap); + int (*camera_configure_cb)(struct device *cam_dev, + struct camera_fb_cfg *fb_cfg); + int (*camera_start_cb)(struct device *cam_dev, + enum camera_mode mode, void **bufs, + u8_t buf_num, camera_capture_cb cb); + int (*camera_acquire_fb_cb)(struct device *cam_dev, + void **fb, s32_t timeout); + int (*camera_release_fb_cb)(struct device *cam_dev, + void *fb); +}; + +/** + * @brief Power on/off camera + * + * @param cam_dev Pointer to camera device structure + * + * @param power true on power on else power off + * + * @retval 0 on success else negative errno code. + */ +__syscall int camera_power(struct device *cam_dev, + bool power); + +static inline int z_impl_camera_power(struct device *cam_dev, + bool power) +{ + const struct camera_driver_api *api = cam_dev->driver_api; + + return api->camera_power_cb(cam_dev, power); +} + +/** + * @brief Power reset camera + * + * @param cam_dev Pointer to camera device structure + * + * @retval 0 on success else negative errno code. + */ +__syscall int camera_reset(struct device *cam_dev); + +static inline int z_impl_camera_reset(struct device *cam_dev) +{ + const struct camera_driver_api *api = cam_dev->driver_api; + + return api->camera_reset_cb(cam_dev); +} + +/** + * @brief Get capability of camera + * + * @param cam_dev Pointer to camera device structure + * + * @param cap Pointer to camera capability structure + * filled by camera driver + * + * @retval 0 on success else negative errno code. + */ +__syscall int camera_get_cap(struct device *cam_dev, + struct camera_capability *cap); + +static inline int z_impl_camera_get_cap(struct device *cam_dev, + struct camera_capability *cap) +{ + const struct camera_driver_api *api = cam_dev->driver_api; + + return api->camera_get_cap_cb(cam_dev, cap); +} + +/** + * @brief Configure frame buffer of camera + * + * @param cam_dev Pointer to camera device structure + * + * @param fb_cfg Pointer to frame buffer configuration + * structure of camera + * + * @retval 0 on success else negative errno code. + */ +__syscall int camera_configure(struct device *cam_dev, + struct camera_fb_cfg *fb_cfg); + +static inline int z_impl_camera_configure(struct device *cam_dev, + struct camera_fb_cfg *fb_cfg) +{ + const struct camera_driver_api *api = cam_dev->driver_api; + + return api->camera_configure_cb(cam_dev, fb_cfg); +} + +/** + * @brief Start camera to capture frames to frame buffer(s) + * + * @param cam_dev Pointer to camera device structure + * + * @param mode Select camera to work on capture mode + * or preview mode + * + * @param bufs Array of buffers for camera to store frames + * + * @param buf_num Number of elements of Array of buffers + * which at lease be one + * + * @param cb Callback function when one frame is received + * which could be NULL + * + * @retval 0 on success else negative errno code. + */ +__syscall int camera_start(struct device *cam_dev, + enum camera_mode mode, void **bufs, u8_t buf_num, + camera_capture_cb cb); + +static inline int z_impl_camera_start(struct device *cam_dev, + enum camera_mode mode, void **bufs, u8_t buf_num, + camera_capture_cb cb) +{ + const struct camera_driver_api *api = cam_dev->driver_api; + + return api->camera_start_cb(cam_dev, mode, bufs, buf_num, cb); +} + +/** + * @brief Acquire one frame from camera + * + * @param cam_dev Pointer to camera device structure + * + * @param fb Pointer to the address of pointer to frame acquired + * + * @param timeout Most ticks to wait for one frame acquired + * + * @retval 0 on frame acquired else negative errno code with + * no frame acquired. + */ +__syscall int camera_acquire_fb(struct device *cam_dev, + void **fb, s32_t timeout); + +static inline int z_impl_camera_acquire_fb( + struct device *cam_dev, void **fb, s32_t timeout) +{ + struct camera_driver_api *api = + (struct camera_driver_api *)cam_dev->driver_api; + + return api->camera_acquire_fb_cb(cam_dev, fb, timeout); +} + +/** + * @brief Release one buffer for camera to store frame to receive + * + * @param cam_dev Pointer to camera device structure + * + * @param fb Pointer to the buffer to release + * + * @retval 0 on buffer released else negative errno code with + * buffer release is rejected. + */ +__syscall int camera_release_fb(struct device *cam_dev, + void *fb); + +static inline int z_impl_camera_release_fb( + struct device *cam_dev, void *fb) +{ + struct camera_driver_api *api = + (struct camera_driver_api *)cam_dev->driver_api; + + return api->camera_release_fb_cb(cam_dev, fb); +} + +/** + * @brief Get primary camera device + * + * @retval pointer to primary camera device, NULL if not found. + */ +struct device *camera_get_primary(void); + +/** + * @brief Get secondary camera device + * + * @retval pointer to secondary camera device, NULL if not found. + */ +struct device *camera_get_secondary(void); + +#include + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_CAMERA_H_*/ diff --git a/include/drivers/camera_drv.h b/include/drivers/camera_drv.h new file mode 100644 index 000000000000..e9d7305e00e4 --- /dev/null +++ b/include/drivers/camera_drv.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public API for camera drivers + */ + +#ifndef ZEPHYR_INCLUDE_CAMERA_DRV_H_ +#define ZEPHYR_INCLUDE_CAMERA_DRV_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @struct camera_driver_data + * @brief Structure to describe camera driver data + * Camera driver is supposed contain this data. + */ +struct camera_driver_data { + struct camera_fb_attr fb_attr; + struct camera_capability cap; + camera_capture_cb customer_cb; + enum camera_mode mode; + enum camera_id id; + + /*Image sensor device associated to + * this camera, whihc could be no present + * for USB camera, PCIe camera.. + */ + struct device *sensor_dev; + + /*Point to the camera driver priviate data*/ + u32_t priv[]; +}; + +/** + * @brief Alloc camera driver data + * This function is called by camera driver. + * + * @param priv_size size of private data of camera driver + * + * @param id Primary camera or secondary camera + * + * @param clear TRUE to clear camera driver data + * + * @retval camera driver data. + * + */ +struct camera_driver_data *camera_drv_data_alloc( + u32_t priv_size, enum camera_id id, bool clear); + + +/** + * @brief Get camera driver private data + * This function is called by camera driver. + * + * @param data Pointer to camera driver structure + * + * @retval camera driver private data. + * + */ +static inline void *camera_data_priv( + struct camera_driver_data *data) +{ + return (void *)data->priv; +} + +/** + * @brief Default function to get capability of camera + * This function is called by camera driver. + * + * @param cam_dev Pointer to camera device structure + * + * @param cap Pointer to camera capability structure + * filled by camera driver + * + * @retval 0 on success else negative errno code. + */ +int camera_dev_get_cap(struct device *cam_dev, + struct camera_capability *cap); + +/** + * @brief Default funciotn to configure frame buffer of camera + * This function is called by camera driver. + * + * @param cam_dev Pointer to camera device structure + * + * @param fb_cfg Pointer to frame buffer configuration + * structure of camera + * + * @retval 0 on success else negative errno code. + */ + +int camera_dev_configure(struct device *cam_dev, + struct camera_fb_cfg *fb_cfg); + + +/** + * @brief Register camera device to camera subsys + * This function is called by camera driver. + * + * @param cam_dev Pointer to camera device structure + * + * @retval 0 on success else negative errno code. + * + */ +int camera_dev_register(struct device *cam_dev); + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_CAMERA_SRV_H_*/ diff --git a/include/drivers/camera_generic.h b/include/drivers/camera_generic.h new file mode 100644 index 000000000000..6a0b55970dff --- /dev/null +++ b/include/drivers/camera_generic.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public API for camera drivers + */ + +#ifndef ZEPHYR_INCLUDE_CAMERA_GENERIC_H_ +#define ZEPHYR_INCLUDE_CAMERA_GENERIC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(CONFIG_IMAGE_SENSOR_INIT_PRIO) &&\ + defined(CONFIG_CAMERA_INIT_PRIO)) +#if (CONFIG_IMAGE_SENSOR_INIT_PRIO >= CONFIG_CAMERA_INIT_PRIO) +#error Image sensor initalization must be earlier than +#error camera initalization. +#endif +#endif + +/*In order to support multiple cameras on device.*/ +#define CAMERA_MAX_NUMBER 2 + +/*For multiple cameras on board, the dts LABEL of + * primary camera should be named "primary". + */ +#define CAMERA_PRIMARY_LABEL "primary" + +/*For multiple cameras on board, the dts LABEL of + * secondary camera should be named "secondary". + */ +#define CAMERA_SECONDARY_LABEL "secondary" + +/** + * @enum camera_id + * @brief Enumeration with camera id to identify + * if the camera is promary camera or secondary camera. + * + */ +enum camera_id { + CAMERA_NULL_ID = 0, + CAMERA_PRIMARY_ID = 1, + CAMERA_SECONDARY_ID = 2 +}; + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_CAMERA_GENERIC_H_*/ diff --git a/include/drivers/display.h b/include/drivers/display.h index ee287af24bde..47946a3244bd 100644 --- a/include/drivers/display.h +++ b/include/drivers/display.h @@ -33,6 +33,8 @@ enum display_pixel_format { PIXEL_FORMAT_MONO10 = BIT(2), /* 1=Black 0=White */ PIXEL_FORMAT_ARGB_8888 = BIT(3), PIXEL_FORMAT_RGB_565 = BIT(4), + PIXEL_FORMAT_YUV_420 = BIT(5), + PIXEL_FORMAT_YUV_422 = BIT(6) }; enum display_screen_info { diff --git a/include/drivers/image_sensor.h b/include/drivers/image_sensor.h new file mode 100644 index 000000000000..4499a1406b2b --- /dev/null +++ b/include/drivers/image_sensor.h @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2019 NXP Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_IMG_SENSOR_H_ +#define ZEPHYR_INCLUDE_IMG_SENSOR_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define IMAGE_SENSOR0_NAME "zephyr-img-sensor0" +#define IMAGE_SENSOR1_NAME "zephyr-img-sensor1" + +enum img_sensor_effect { + IMG_EFFECT_NORMAL, + IMG_EFFECT_NEGATIVE, +}; + +enum bytes_width { + W1B = 1, + W2B = 2, + W4B = 4 +}; + +struct img_sensor_reg { + u32_t reg; + enum bytes_width w_reg; + u32_t value; + enum bytes_width w_value; +}; + +/*Public data placed at the beginning of driver data of device*/ +struct img_sensor_host { + /*Camera module associated to*/ + enum camera_id id; + /*I2C device used for configuration*/ + struct device *i2c; + /*GPIO device used for Power control, optional*/ + struct device *pwr_gpio; + /*GPIO pin used for Power control, optional*/ + u32_t pin; + /*GPIO level used for Power control, optional*/ + int flag; +}; + +struct img_sensor_capability { + u32_t pixformat_support; + u16_t width_max; + u16_t height_max; +}; + +struct img_sensor_client { + u16_t i2c_addr; + u32_t sensor_id; + enum bytes_width w_sensor_id; + u32_t id_reg; + enum bytes_width w_id_reg; + int contrast_level; + int bright_level; + float r_gain_db; + float g_gain_db; + float b_gain_db; + u16_t width; + u16_t height; + enum img_sensor_effect effect; + enum display_pixel_format pixformat; + struct img_sensor_capability cap; +}; + +struct img_sensor_data { + struct img_sensor_host host_info; + struct img_sensor_client client_info; +}; + +/*Master clock could be enabled by camer driver + * before accessing CMOS. + * Or disabled by camera driver when system is + * entering sleep mode. + */ +typedef void (*img_sensor_mclk_enable)(struct device *cam_dev, bool enable); + +struct img_sensor_driver_api { + int (*img_sensor_reset_cb)(struct device *dev); + int (*img_sensor_get_cap_cb)(struct device *dev, + struct img_sensor_capability *cap); + int (*img_sensor_read_reg_cb)(struct device *dev, + u32_t addr, u8_t addr_width, u8_t *data, u8_t data_width); + int (*img_sensor_write_reg_cb)(struct device *dev, + u32_t addr, u8_t addr_width, u32_t data, u8_t data_width); + int (*img_sensor_set_pixformat_cb)(struct device *dev, + enum display_pixel_format pixformat); + int (*img_sensor_set_framesize_cb)(struct device *dev, + u16_t width, u16_t height); + int (*img_sensor_set_contrast_cb)(struct device *dev, + int level); + int (*img_sensor_set_brightness_cb)(struct device *dev, + int level); + int (*img_sensor_set_rgb_gain_cb)(struct device *dev, + float r_gain_db, float g_gain_db, float b_gain_db); + int (*img_sensor_set_effect_cb)(struct device *dev, + enum img_sensor_effect effect); + int (*img_sensor_config_cb)(struct device *dev); +}; + +struct img_sensor_info { + sys_dnode_t node; + struct img_sensor_client sensor_client; + const struct img_sensor_driver_api *sensor_api; +}; + +void img_sensor_support_add( + struct img_sensor_info *img_sensor); + +__syscall int img_sensor_reset(struct device *dev); + +static inline int z_impl_img_sensor_reset(struct device *dev) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_reset_cb(dev); +} + +__syscall int img_sensor_get_cap(struct device *dev, + struct img_sensor_capability *cap); + +static inline int z_impl_img_sensor_get_cap(struct device *dev, + struct img_sensor_capability *cap) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_get_cap_cb(dev, cap); +} + + +__syscall int img_sensor_read_reg(struct device *dev, + u32_t addr, u8_t addr_width, u8_t *data, u8_t data_width); + +static inline int z_impl_img_sensor_read_reg(struct device *dev, + u32_t addr, u8_t addr_width, u8_t *data, u8_t data_width) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_read_reg_cb(dev, + addr, addr_width, data, data_width); +} + +__syscall int img_sensor_write_reg(struct device *dev, + u32_t addr, u8_t addr_width, u32_t data, u8_t data_width); + +static inline int z_impl_img_sensor_write_reg(struct device *dev, + u32_t addr, u8_t addr_width, u32_t data, u8_t data_width) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_write_reg_cb(dev, + addr, addr_width, data, data_width); +} + +__syscall int img_sensor_set_pixformat(struct device *dev, + enum display_pixel_format pixformat); + +static inline int z_impl_img_sensor_set_pixformat(struct device *dev, + enum display_pixel_format pixformat) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_set_pixformat_cb(dev, pixformat); +} +__syscall int img_sensor_set_framesize(struct device *dev, + u16_t width, u16_t height); + +static inline int z_impl_img_sensor_set_framesize(struct device *dev, + u16_t width, u16_t height) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_set_framesize_cb(dev, width, height); +} +__syscall int img_sensor_set_contrast(struct device *dev, + int level); + +static inline int z_impl_img_sensor_set_contrast(struct device *dev, + int level) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_set_contrast_cb(dev, level); +} + +__syscall int img_sensor_set_brightness(struct device *dev, + int level); + +static inline int z_impl_img_sensor_set_brightness(struct device *dev, + int level) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_set_brightness_cb(dev, level); +} + +__syscall int img_sensor_set_gain(struct device *dev, + float r_gain_db, float g_gain_db, float b_gain_db); + +static inline int z_impl_img_sensor_set_gain(struct device *dev, + float r_gain_db, float g_gain_db, float b_gain_db) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_set_rgb_gain_cb(dev, + r_gain_db, g_gain_db, b_gain_db); +} + +__syscall int img_sensor_set_effect(struct device *dev, + enum img_sensor_effect effect); + +static inline int z_impl_img_sensor_set_effect(struct device *dev, + enum img_sensor_effect effect) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_set_effect_cb(dev, effect); +} + +__syscall int img_sensor_configure(struct device *dev); + +static inline int z_impl_img_sensor_configure(struct device *dev) +{ + const struct img_sensor_driver_api *api = dev->driver_api; + + return api->img_sensor_config_cb(dev); +} + +struct device *img_sensor_get_prime(void); + +struct device *img_sensor_get_by_id(enum camera_id id); + +struct device *img_sensor_scan(enum camera_id id); + +#include + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_IMG_SENSOR_H_ */ diff --git a/include/dt-bindings/clock/imx_ccm.h b/include/dt-bindings/clock/imx_ccm.h index 066c7f4881b5..54bc26078171 100644 --- a/include/dt-bindings/clock/imx_ccm.h +++ b/include/dt-bindings/clock/imx_ccm.h @@ -8,12 +8,13 @@ #define ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_IMX_CCM_H_ #define IMX_CCM_CORESYS_CLK 0 -#define IMX_CCM_PLATFORM_CLK 1 +#define IMX_CCM_PLATFORM_CLK 1 #define IMX_CCM_BUS_CLK 2 #define IMX_CCM_LPUART_CLK 3 #define IMX_CCM_LPI2C_CLK 4 #define IMX_CCM_LPSPI_CLK 5 #define IMX_CCM_USDHC1_CLK 6 #define IMX_CCM_USDHC2_CLK 7 +#define IMX_CCM_CSI_CLK 8 #endif /* ZEPHYR_INCLUDE_DT_BINDINGS_CLOCK_IMX_CCM_H_ */ diff --git a/samples/camera/CMakeLists.txt b/samples/camera/CMakeLists.txt new file mode 100644 index 000000000000..67b0185cc0dd --- /dev/null +++ b/samples/camera/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(camera) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/camera/README.rst b/samples/camera/README.rst new file mode 100644 index 000000000000..70624a55ba9f --- /dev/null +++ b/samples/camera/README.rst @@ -0,0 +1,30 @@ +.. camera: + +Camera Sample Application +################################### + +Overview +******** +This project demos camera samples. +It should work with any platform featuring a I2C peripheral interface and a CMOS sensor interface. +It does not work on QEMU. +If there is a display device on the board, the demo displays camera stream on the display device. + +Requirements +************ +This project requires a CMOS sensor connected to board. + +Building and Running +******************** +This sample can be built for a ``mimxrt1050_evk`` board with mt9m114 CMOS sensor connected. + +.. zephyr-app-commands:: + :zephyr-app: samples/camera + :board: mimxrt1050_evk + :goals: build flash + +To run this sample, a CMOS sensor should be connected to the board. +The sample outputs the FPS(frames per second) number on the console in capture +mode and preview mode periodically. +If there is a display device present on the board, the sample outputs the camera +stream on the display device. diff --git a/samples/camera/prj.conf b/samples/camera/prj.conf new file mode 100644 index 000000000000..bca536168b0a --- /dev/null +++ b/samples/camera/prj.conf @@ -0,0 +1,3 @@ +CONFIG_CAMERA=y +CONFIG_I2C=y +CONFIG_DISPLAY=y diff --git a/samples/camera/sample.yaml b/samples/camera/sample.yaml new file mode 100644 index 000000000000..038acfd7c32f --- /dev/null +++ b/samples/camera/sample.yaml @@ -0,0 +1,8 @@ +sample: + name: Camera Sample +tests: + sample.camera: + harness: camera + tags: camera + platform_whitelist: mimxrt1050_evk + depends_on: camera i2c display diff --git a/samples/camera/src/main.c b/samples/camera/src/main.c new file mode 100644 index 000000000000..69507feae539 --- /dev/null +++ b/samples/camera/src/main.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2019 NXP Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +/* _______ _________ + *| |<-------|head| | + *| | |____| |___________ ______ + *| | |tail|<---|__camer_IP_|<---|_CMOS_| + *| |------->|____|____| + *|_______| |____|____| + * Display Camera + */ + +#define CAMERA_DEMO_FB_MAX_SIZE 0x40000 +#define CAMERA_DEMO_FB_MAX_NUM 4 +char gfb[CAMERA_DEMO_FB_MAX_SIZE * CAMERA_DEMO_FB_MAX_NUM] __aligned(64); +struct k_sem gsem; + +static void capture_cb(void *fb, int w, int h, int bpp) +{ + k_sem_give(&gsem); +} + +void main(void) +{ + int ret, i; + struct device *camera_dev = camera_get_primary(); + struct device *display_dev = + device_get_binding(CONFIG_CAMERA_DISPLAY_DEV_NAME); + struct display_capabilities diaplay_cap; + struct display_buffer_descriptor diaplay_dsc; + struct camera_capability camera_cap; + struct camera_fb_cfg camera_cfg; + void *fb[CAMERA_DEMO_FB_MAX_NUM]; + u8_t bpp; + s64_t start_tick; + double frames = 0; + double ms; + + k_sem_init(&gsem, 0, 1); + if (camera_dev == NULL) { + printf("Failed to get camera device.\n"); + return; + } + + ret = camera_power(camera_dev, true); + if (!ret) { + printf("%s camera power on successfully!\r\n", + camera_dev->config->name); + } else { + printf("%s camera power on failed!\r\n", + camera_dev->config->name); + + return; + } + + /*camera_reset is option, just for reset API test.*/ + ret = camera_reset(camera_dev); + if (!ret) { + printf("%s camera reset successfully!\r\n", + camera_dev->config->name); + } else { + printf("%s camera reset failed!\r\n", + camera_dev->config->name); + + return; + } + + ret = camera_get_cap(camera_dev, &camera_cap); + if (!ret) { + printf("%s camera get capability successfully!\r\n", + camera_dev->config->name); + } else { + printf("%s camera get capability failed!\r\n", + camera_dev->config->name); + + return; + } + + if (display_dev == NULL) { + printf("Failed to get display device.\n"); + } else { + display_get_capabilities(display_dev, &diaplay_cap); + } + + for (i = 0; i < CAMERA_DEMO_FB_MAX_NUM; i++) { + fb[i] = &gfb[i * CAMERA_DEMO_FB_MAX_SIZE]; + } + + if (display_dev) { + if (diaplay_cap.current_pixel_format == + PIXEL_FORMAT_RGB_888) { + bpp = 3; + } else if (diaplay_cap.current_pixel_format == + PIXEL_FORMAT_ARGB_8888) { + bpp = 4; + } else if (diaplay_cap.current_pixel_format == + PIXEL_FORMAT_RGB_565) { + bpp = 2; + } else { + printf("Unsupported pix format %d for camera\n", + diaplay_cap.current_pixel_format); + return; + } + + if (!(camera_cap.pixformat_support & + diaplay_cap.current_pixel_format)) { + printf("The display pixel format 0x%08x is not" + " supported by camera format supported: 0x%08x\r\n", + diaplay_cap.current_pixel_format, + camera_cap.pixformat_support); + + return; + } + + if (diaplay_cap.x_resolution > camera_cap.width_max || + diaplay_cap.y_resolution > camera_cap.height_max) { + printf("The display resolution exceeds" + " the camera max resolution %d X %d\r\n", + camera_cap.width_max, camera_cap.height_max); + + return; + } + + camera_cfg.cfg_mode = CAMERA_USER_CFG; + camera_cfg.fb_attr.width = diaplay_cap.x_resolution; + camera_cfg.fb_attr.height = diaplay_cap.y_resolution; + camera_cfg.fb_attr.bpp = bpp; + camera_cfg.fb_attr.pixformat = diaplay_cap.current_pixel_format; + + diaplay_dsc.buf_size = camera_cfg.fb_attr.width * + camera_cfg.fb_attr.height * bpp; + diaplay_dsc.width = camera_cfg.fb_attr.width; + diaplay_dsc.height = camera_cfg.fb_attr.height; + diaplay_dsc.pitch = camera_cfg.fb_attr.width; + } else { + camera_cfg.cfg_mode = CAMERA_DEFAULT_CFG; + } + + ret = camera_configure(camera_dev, &camera_cfg); + if (!ret) { + printf("%s camera configure successfully!\r\n", + camera_dev->config->name); + } else { + printf("%s camera configure failed!\r\n", + camera_dev->config->name); + } + +loop_again: + printf("Enter capture mode\r\n"); + start_tick = z_tick_get(); + while (1) { + void *camera_fb; + + ret = camera_start(camera_dev, + CAMERA_CAPTURE_MODE, &fb[0], 1, capture_cb); + if (!ret) { + k_sem_take(&gsem, K_FOREVER); + ret = camera_acquire_fb(camera_dev, + &camera_fb, K_FOREVER); + if (!ret) { + if (display_dev) { + display_write(display_dev, 0, 0, + &diaplay_dsc, camera_fb); + } + frames++; + printf("%d", (int)frames); + printf("\b"); + if (frames >= 10) { + printf("\b"); + } + if (frames >= 100) { + printf("\b"); + } + if (unlikely(frames == 300)) { + ms = __ticks_to_ms( + z_tick_get() - start_tick); + printf("\r\nCapture mode FPS: %f\r\n", + (frames / (ms / 1000))); + start_tick = z_tick_get(); + frames = 0; + break; + } + } else { + printf("Camera get frame buffer failed!\r\n"); + } + } else { + printf("Camera capture failed!\r\n"); + } + } + + printf("Enter preview mode\r\n"); + ret = camera_start(camera_dev, CAMERA_PREVIEW_MODE, &fb[0], + CAMERA_DEMO_FB_MAX_NUM, 0); + if (ret) { + printf("Preview start failed\r\n"); + goto loop_again; + } + + start_tick = z_tick_get(); + while (1) { + void *camera_fb; + + ret = camera_acquire_fb(camera_dev, &camera_fb, K_FOREVER); + if (!ret) { + if (display_dev) { + display_write(display_dev, 0, 0, + &diaplay_dsc, camera_fb); + } + + frames++; + printf("%d", (int)frames); + printf("\b"); + if (frames >= 10) { + printf("\b"); + } + if (frames >= 100) { + printf("\b"); + } + if (unlikely(frames == 300)) { + ms = __ticks_to_ms( + z_tick_get() - start_tick); + printf("\r\nPreview mode FPS: %f\r\n", + (frames / (ms / 1000))); + start_tick = z_tick_get(); + frames = 0; + break; + } + + camera_release_fb(camera_dev, camera_fb); + } else { + printf("Preview get frame buffer failed!\r\n"); + } + } + + goto loop_again; +} diff --git a/samples/index.rst b/samples/index.rst index 792fe9c9b440..7961bd691426 100644 --- a/samples/index.rst +++ b/samples/index.rst @@ -23,6 +23,7 @@ Samples and Demos shields/* portability/* gui/* + camera/* .. comment To add a new sample document, please use the template available under diff --git a/soc/arm/nxp_imx/rt/Kconfig.defconfig.series b/soc/arm/nxp_imx/rt/Kconfig.defconfig.series index 4dceeda7b6bf..05c9722d5b8f 100644 --- a/soc/arm/nxp_imx/rt/Kconfig.defconfig.series +++ b/soc/arm/nxp_imx/rt/Kconfig.defconfig.series @@ -141,6 +141,13 @@ config USB_DC_NXP_EHCI endif # USB +if CAMERA + +config NXP_MCUX_CSI + default y if HAS_MCUX_CSI + +endif # CAMERA + source "soc/arm/nxp_imx/rt/Kconfig.defconfig.mimxrt*" endif # SOC_SERIES_IMX_RT diff --git a/soc/arm/nxp_imx/rt/Kconfig.soc b/soc/arm/nxp_imx/rt/Kconfig.soc index 8ea79667d0d1..9dfb2a03b113 100644 --- a/soc/arm/nxp_imx/rt/Kconfig.soc +++ b/soc/arm/nxp_imx/rt/Kconfig.soc @@ -70,6 +70,7 @@ config SOC_MIMXRT1051 select HAS_MCUX_USB_EHCI select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 + select HAS_MCUX_CSI config SOC_MIMXRT1052 bool "SOC_MIMXRT1052" @@ -93,6 +94,7 @@ config SOC_MIMXRT1052 select HAS_MCUX_USB_EHCI select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 + select HAS_MCUX_CSI config SOC_MIMXRT1061 bool "SOC_MIMXRT1061" @@ -113,6 +115,7 @@ config SOC_MIMXRT1061 select HAS_MCUX_USB_EHCI select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 + select HAS_MCUX_CSI config SOC_MIMXRT1062 bool "SOC_MIMXRT1062" @@ -135,6 +138,7 @@ config SOC_MIMXRT1062 select HAS_MCUX_USB_EHCI select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 + select HAS_MCUX_CSI config SOC_MIMXRT1064 bool "SOC_MIMXRT1064" @@ -157,6 +161,7 @@ config SOC_MIMXRT1064 select HAS_MCUX_USB_EHCI select HAS_MCUX_USDHC1 select HAS_MCUX_USDHC2 + select HAS_MCUX_CSI endchoice diff --git a/soc/arm/nxp_imx/rt/soc.c b/soc/arm/nxp_imx/rt/soc.c index d0e4dc5f0ef1..2bd5ca4f97be 100644 --- a/soc/arm/nxp_imx/rt/soc.c +++ b/soc/arm/nxp_imx/rt/soc.c @@ -236,6 +236,20 @@ void imxrt_usdhc_pinmux(u16_t nusdhc, bool init, } #endif +#ifdef CONFIG_NXP_MCUX_CSI + +static csi_mclk_enable_cb g_csi_mclk_enable_cb; +void imxrt_csi_mclk_cb_register(csi_mclk_enable_cb cb) +{ + g_csi_mclk_enable_cb = cb; +} + +void imxrt_csi_mclk_enable(bool enable) +{ + if (g_csi_mclk_enable_cb) + g_csi_mclk_enable_cb(enable); +} +#endif /** * * @brief Perform basic hardware initialization diff --git a/soc/arm/nxp_imx/rt/soc.h b/soc/arm/nxp_imx/rt/soc.h index 9206c215aada..14b88b7cf0b2 100644 --- a/soc/arm/nxp_imx/rt/soc.h +++ b/soc/arm/nxp_imx/rt/soc.h @@ -36,6 +36,13 @@ void imxrt_usdhc_pinmux_cb_register(usdhc_pin_cfg_cb cb); #endif +#ifdef CONFIG_NXP_MCUX_CSI +typedef void (*csi_mclk_enable_cb)(bool enable); + +void imxrt_csi_mclk_enable(bool enable); + +void imxrt_csi_mclk_cb_register(csi_mclk_enable_cb cb); +#endif #endif /* !_ASMLANGUAGE */ #ifdef __cplusplus