| /* | 
 |  * Copyright (c) 2024 Felipe Neves | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #define DT_DRV_COMPAT galaxycore_gc2145 | 
 | #include <zephyr/kernel.h> | 
 | #include <zephyr/device.h> | 
 |  | 
 | #include <zephyr/drivers/video.h> | 
 | #include <zephyr/drivers/video-controls.h> | 
 | #include <zephyr/drivers/i2c.h> | 
 | #include <zephyr/drivers/gpio.h> | 
 |  | 
 | #include <zephyr/logging/log.h> | 
 | LOG_MODULE_REGISTER(video_gc2145, CONFIG_VIDEO_LOG_LEVEL); | 
 |  | 
 | #define GC2145_REG_AMODE1               0x17 | 
 | #define GC2145_AMODE1_WINDOW_MASK       0xFC | 
 | #define GC2145_REG_AMODE1_DEF           0x14 | 
 | #define GC2145_REG_OUTPUT_FMT           0x84 | 
 | #define GC2145_REG_OUTPUT_FMT_MASK      0x1F | 
 | #define GC2145_REG_OUTPUT_FMT_RGB565    0x06 | 
 | #define GC2145_REG_OUTPUT_FMT_YCBYCR    0x02 | 
 | #define GC2145_REG_SYNC_MODE            0x86 | 
 | #define GC2145_REG_SYNC_MODE_DEF        0x23 | 
 | #define GC2145_REG_SYNC_MODE_COL_SWITCH 0x10 | 
 | #define GC2145_REG_SYNC_MODE_ROW_SWITCH 0x20 | 
 | #define GC2145_REG_RESET                0xFE | 
 | #define GC2145_REG_SW_RESET             0x80 | 
 | #define GC2145_PID_VAL                  0x21 | 
 | #define GC2145_REV_VAL                  0x55 | 
 | #define GC2145_SET_P0_REGS              0x00 | 
 | #define GC2145_REG_CROP_ENABLE          0x90 | 
 | #define GC2145_CROP_SET_ENABLE          0x01 | 
 | #define GC2145_REG_BLANK_WINDOW_BASE    0x09 | 
 | #define GC2145_REG_WINDOW_BASE          0x91 | 
 | #define GC2145_REG_SUBSAMPLE            0x99 | 
 | #define GC2145_REG_SUBSAMPLE_MODE       0x9A | 
 | #define GC2145_SUBSAMPLE_MODE_SMOOTH    0x0E | 
 |  | 
 | #define UXGA_HSIZE 1600 | 
 | #define UXGA_VSIZE 1200 | 
 |  | 
 | struct gc2145_reg { | 
 | 	uint8_t addr; | 
 | 	uint8_t value; | 
 | }; | 
 |  | 
 | static const struct gc2145_reg default_regs[] = { | 
 | 	{0xfe, 0xf0}, | 
 | 	{0xfe, 0xf0}, | 
 | 	{0xfe, 0xf0}, | 
 | 	{0xfc, 0x06}, | 
 | 	{0xf6, 0x00}, | 
 | 	{0xf7, 0x1d}, | 
 | 	{0xf8, 0x85}, | 
 | 	{0xfa, 0x00}, | 
 | 	{0xf9, 0xfe}, | 
 | 	{0xf2, 0x00}, | 
 |  | 
 | 	/* ISP settings */ | 
 | 	{0xfe, 0x00}, | 
 | 	{0x03, 0x04}, | 
 | 	{0x04, 0xe2}, | 
 |  | 
 | 	{0x09, 0x00}, | 
 | 	{0x0a, 0x00}, | 
 |  | 
 | 	{0x0b, 0x00}, | 
 | 	{0x0c, 0x00}, | 
 |  | 
 | 	{0x0d, 0x04}, /* Window height */ | 
 | 	{0x0e, 0xc0}, | 
 |  | 
 | 	{0x0f, 0x06}, /* Window width */ | 
 | 	{0x10, 0x52}, | 
 |  | 
 | 	{0x99, 0x11}, /* Subsample */ | 
 | 	{0x9a, 0x0E}, /* Subsample mode */ | 
 |  | 
 | 	{0x12, 0x2e}, | 
 | 	{0x17, 0x14}, /* Analog Mode 1 (vflip/mirror[1:0]) */ | 
 | 	{0x18, 0x22}, /* Analog Mode 2 */ | 
 | 	{0x19, 0x0e}, | 
 | 	{0x1a, 0x01}, | 
 | 	{0x1b, 0x4b}, | 
 | 	{0x1c, 0x07}, | 
 | 	{0x1d, 0x10}, | 
 | 	{0x1e, 0x88}, | 
 | 	{0x1f, 0x78}, | 
 | 	{0x20, 0x03}, | 
 | 	{0x21, 0x40}, | 
 | 	{0x22, 0xa0}, | 
 | 	{0x24, 0x16}, | 
 | 	{0x25, 0x01}, | 
 | 	{0x26, 0x10}, | 
 | 	{0x2d, 0x60}, | 
 | 	{0x30, 0x01}, | 
 | 	{0x31, 0x90}, | 
 | 	{0x33, 0x06}, | 
 | 	{0x34, 0x01}, | 
 | 	{0x80, 0x7f}, | 
 | 	{0x81, 0x26}, | 
 | 	{0x82, 0xfa}, | 
 | 	{0x83, 0x00}, | 
 | 	{GC2145_REG_OUTPUT_FMT, 0x06}, | 
 | 	{GC2145_REG_SYNC_MODE, 0x23}, | 
 | 	{0x88, 0x03}, | 
 | 	{0x89, 0x03}, | 
 | 	{0x85, 0x08}, | 
 | 	{0x8a, 0x00}, | 
 | 	{0x8b, 0x00}, | 
 | 	{0xb0, 0x55}, | 
 | 	{0xc3, 0x00}, | 
 | 	{0xc4, 0x80}, | 
 | 	{0xc5, 0x90}, | 
 | 	{0xc6, 0x3b}, | 
 | 	{0xc7, 0x46}, | 
 | 	{0xec, 0x06}, | 
 | 	{0xed, 0x04}, | 
 | 	{0xee, 0x60}, | 
 | 	{0xef, 0x90}, | 
 | 	{0xb6, 0x01}, | 
 |  | 
 | 	{0x90, 0x01}, | 
 | 	{0x91, 0x00}, | 
 | 	{0x92, 0x00}, | 
 | 	{0x93, 0x00}, | 
 | 	{0x94, 0x00}, | 
 | 	{0x95, 0x02}, | 
 | 	{0x96, 0x58}, | 
 | 	{0x97, 0x03}, | 
 | 	{0x98, 0x20}, | 
 | 	{0x99, 0x22}, | 
 | 	{0x9a, 0x0E}, | 
 |  | 
 | 	{0x9b, 0x00}, | 
 | 	{0x9c, 0x00}, | 
 | 	{0x9d, 0x00}, | 
 | 	{0x9e, 0x00}, | 
 | 	{0x9f, 0x00}, | 
 | 	{0xa0, 0x00}, | 
 | 	{0xa1, 0x00}, | 
 | 	{0xa2, 0x00}, | 
 |  | 
 | 	/* BLK Settings */ | 
 | 	{0xfe, 0x00}, | 
 | 	{0x40, 0x42}, | 
 | 	{0x41, 0x00}, | 
 | 	{0x43, 0x5b}, | 
 | 	{0x5e, 0x00}, | 
 | 	{0x5f, 0x00}, | 
 | 	{0x60, 0x00}, | 
 | 	{0x61, 0x00}, | 
 | 	{0x62, 0x00}, | 
 | 	{0x63, 0x00}, | 
 | 	{0x64, 0x00}, | 
 | 	{0x65, 0x00}, | 
 | 	{0x66, 0x20}, | 
 | 	{0x67, 0x20}, | 
 | 	{0x68, 0x20}, | 
 | 	{0x69, 0x20}, | 
 | 	{0x76, 0x00}, | 
 | 	{0x6a, 0x08}, | 
 | 	{0x6b, 0x08}, | 
 | 	{0x6c, 0x08}, | 
 | 	{0x6d, 0x08}, | 
 | 	{0x6e, 0x08}, | 
 | 	{0x6f, 0x08}, | 
 | 	{0x70, 0x08}, | 
 | 	{0x71, 0x08}, | 
 | 	{0x76, 0x00}, | 
 | 	{0x72, 0xf0}, | 
 | 	{0x7e, 0x3c}, | 
 | 	{0x7f, 0x00}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0x48, 0x15}, | 
 | 	{0x49, 0x00}, | 
 | 	{0x4b, 0x0b}, | 
 | 	{0xfe, 0x00}, | 
 |  | 
 | 	/* AEC Settings */ | 
 | 	{0xfe, 0x01}, | 
 | 	{0x01, 0x04}, | 
 | 	{0x02, 0xc0}, | 
 | 	{0x03, 0x04}, | 
 | 	{0x04, 0x90}, | 
 | 	{0x05, 0x30}, | 
 | 	{0x06, 0x90}, | 
 | 	{0x07, 0x30}, | 
 | 	{0x08, 0x80}, | 
 | 	{0x09, 0x00}, | 
 | 	{0x0a, 0x82}, | 
 | 	{0x0b, 0x11}, | 
 | 	{0x0c, 0x10}, | 
 | 	{0x11, 0x10}, | 
 | 	{0x13, 0x68}, | 
 | 	{GC2145_REG_OUTPUT_FMT, 0x00}, | 
 | 	{0x1c, 0x11}, | 
 | 	{0x1e, 0x61}, | 
 | 	{0x1f, 0x35}, | 
 | 	{0x20, 0x40}, | 
 | 	{0x22, 0x40}, | 
 | 	{0x23, 0x20}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0x0f, 0x04}, | 
 | 	{0xfe, 0x01}, | 
 | 	{0x12, 0x30}, | 
 | 	{0x15, 0xb0}, | 
 | 	{0x10, 0x31}, | 
 | 	{0x3e, 0x28}, | 
 | 	{0x3f, 0xb0}, | 
 | 	{0x40, 0x90}, | 
 | 	{0x41, 0x0f}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0x90, 0x6c}, | 
 | 	{0x91, 0x03}, | 
 | 	{0x92, 0xcb}, | 
 | 	{0x94, 0x33}, | 
 | 	{0x95, 0x84}, | 
 | 	{0x97, 0x65}, | 
 | 	{0xa2, 0x11}, | 
 | 	{0xfe, 0x00}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0x80, 0xc1}, | 
 | 	{0x81, 0x08}, | 
 | 	{0x82, 0x05}, | 
 | 	{0x83, 0x08}, | 
 | 	{GC2145_REG_OUTPUT_FMT, 0x0a}, | 
 | 	{GC2145_REG_SYNC_MODE, 0xf0}, | 
 | 	{0x87, 0x50}, | 
 | 	{0x88, 0x15}, | 
 | 	{0x89, 0xb0}, | 
 | 	{0x8a, 0x30}, | 
 | 	{0x8b, 0x10}, | 
 | 	{0xfe, 0x01}, | 
 | 	{0x21, 0x04}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0xa3, 0x50}, | 
 | 	{0xa4, 0x20}, | 
 | 	{0xa5, 0x40}, | 
 | 	{0xa6, 0x80}, | 
 | 	{0xab, 0x40}, | 
 | 	{0xae, 0x0c}, | 
 | 	{0xb3, 0x46}, | 
 | 	{0xb4, 0x64}, | 
 | 	{0xb6, 0x38}, | 
 | 	{0xb7, 0x01}, | 
 | 	{0xb9, 0x2b}, | 
 | 	{0x3c, 0x04}, | 
 | 	{0x3d, 0x15}, | 
 | 	{0x4b, 0x06}, | 
 | 	{0x4c, 0x20}, | 
 | 	{0xfe, 0x00}, | 
 |  | 
 | 	/* Gamma Control */ | 
 | 	{0xfe, 0x02}, | 
 | 	{0x10, 0x09}, | 
 | 	{0x11, 0x0d}, | 
 | 	{0x12, 0x13}, | 
 | 	{0x13, 0x19}, | 
 | 	{0x14, 0x27}, | 
 | 	{0x15, 0x37}, | 
 | 	{0x16, 0x45}, | 
 | 	{GC2145_REG_OUTPUT_FMT, 0x53}, | 
 | 	{0x18, 0x69}, | 
 | 	{0x19, 0x7d}, | 
 | 	{0x1a, 0x8f}, | 
 | 	{0x1b, 0x9d}, | 
 | 	{0x1c, 0xa9}, | 
 | 	{0x1d, 0xbd}, | 
 | 	{0x1e, 0xcd}, | 
 | 	{0x1f, 0xd9}, | 
 | 	{0x20, 0xe3}, | 
 | 	{0x21, 0xea}, | 
 | 	{0x22, 0xef}, | 
 | 	{0x23, 0xf5}, | 
 | 	{0x24, 0xf9}, | 
 | 	{0x25, 0xff}, | 
 | 	{0xfe, 0x00}, | 
 | 	{0xc6, 0x20}, | 
 | 	{0xc7, 0x2b}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0x26, 0x0f}, | 
 | 	{0x27, 0x14}, | 
 | 	{0x28, 0x19}, | 
 | 	{0x29, 0x1e}, | 
 | 	{0x2a, 0x27}, | 
 | 	{0x2b, 0x33}, | 
 | 	{0x2c, 0x3b}, | 
 | 	{0x2d, 0x45}, | 
 | 	{0x2e, 0x59}, | 
 | 	{0x2f, 0x69}, | 
 | 	{0x30, 0x7c}, | 
 | 	{0x31, 0x89}, | 
 | 	{0x32, 0x98}, | 
 | 	{0x33, 0xae}, | 
 | 	{0x34, 0xc0}, | 
 | 	{0x35, 0xcf}, | 
 | 	{0x36, 0xda}, | 
 | 	{0x37, 0xe2}, | 
 | 	{0x38, 0xe9}, | 
 | 	{0x39, 0xf3}, | 
 | 	{0x3a, 0xf9}, | 
 | 	{0x3b, 0xff}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0xd1, 0x32}, | 
 | 	{0xd2, 0x32}, | 
 | 	{0xd3, 0x40}, | 
 | 	{0xd6, 0xf0}, | 
 | 	{0xd7, 0x10}, | 
 | 	{0xd8, 0xda}, | 
 | 	{0xdd, 0x14}, | 
 | 	{0xde, 0x86}, | 
 | 	{0xed, 0x80}, | 
 | 	{0xee, 0x00}, | 
 | 	{0xef, 0x3f}, | 
 | 	{0xd8, 0xd8}, | 
 | 	{0xfe, 0x01}, | 
 | 	{0x9f, 0x40}, | 
 | 	{0xfe, 0x01}, | 
 | 	{0xc2, 0x14}, | 
 | 	{0xc3, 0x0d}, | 
 | 	{0xc4, 0x0c}, | 
 | 	{0xc8, 0x15}, | 
 | 	{0xc9, 0x0d}, | 
 | 	{0xca, 0x0a}, | 
 | 	{0xbc, 0x24}, | 
 | 	{0xbd, 0x10}, | 
 | 	{0xbe, 0x0b}, | 
 | 	{0xb6, 0x25}, | 
 | 	{0xb7, 0x16}, | 
 | 	{0xb8, 0x15}, | 
 | 	{0xc5, 0x00}, | 
 | 	{0xc6, 0x00}, | 
 | 	{0xc7, 0x00}, | 
 | 	{0xcb, 0x00}, | 
 | 	{0xcc, 0x00}, | 
 | 	{0xcd, 0x00}, | 
 | 	{0xbf, 0x07}, | 
 | 	{0xc0, 0x00}, | 
 | 	{0xc1, 0x00}, | 
 | 	{0xb9, 0x00}, | 
 | 	{0xba, 0x00}, | 
 | 	{0xbb, 0x00}, | 
 | 	{0xaa, 0x01}, | 
 | 	{0xab, 0x01}, | 
 | 	{0xac, 0x00}, | 
 | 	{0xad, 0x05}, | 
 | 	{0xae, 0x06}, | 
 | 	{0xaf, 0x0e}, | 
 | 	{0xb0, 0x0b}, | 
 | 	{0xb1, 0x07}, | 
 | 	{0xb2, 0x06}, | 
 | 	{0xb3, 0x17}, | 
 | 	{0xb4, 0x0e}, | 
 | 	{0xb5, 0x0e}, | 
 | 	{0xd0, 0x09}, | 
 | 	{0xd1, 0x00}, | 
 | 	{0xd2, 0x00}, | 
 | 	{0xd6, 0x08}, | 
 | 	{0xd7, 0x00}, | 
 | 	{0xd8, 0x00}, | 
 | 	{0xd9, 0x00}, | 
 | 	{0xda, 0x00}, | 
 | 	{0xdb, 0x00}, | 
 | 	{0xd3, 0x0a}, | 
 | 	{0xd4, 0x00}, | 
 | 	{0xd5, 0x00}, | 
 | 	{0xa4, 0x00}, | 
 | 	{0xa5, 0x00}, | 
 | 	{0xa6, 0x77}, | 
 | 	{0xa7, 0x77}, | 
 | 	{0xa8, 0x77}, | 
 | 	{0xa9, 0x77}, | 
 | 	{0xa1, 0x80}, | 
 | 	{0xa2, 0x80}, | 
 |  | 
 | 	{0xfe, 0x01}, | 
 | 	{0xdf, 0x0d}, | 
 | 	{0xdc, 0x25}, | 
 | 	{0xdd, 0x30}, | 
 | 	{0xe0, 0x77}, | 
 | 	{0xe1, 0x80}, | 
 | 	{0xe2, 0x77}, | 
 | 	{0xe3, 0x90}, | 
 | 	{0xe6, 0x90}, | 
 | 	{0xe7, 0xa0}, | 
 | 	{0xe8, 0x90}, | 
 | 	{0xe9, 0xa0}, | 
 | 	{0xfe, 0x00}, | 
 | 	{0xfe, 0x01}, | 
 | 	{0x4f, 0x00}, | 
 | 	{0x4f, 0x00}, | 
 | 	{0x4b, 0x01}, | 
 | 	{0x4f, 0x00}, | 
 |  | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x71}, | 
 | 	{0x4e, 0x01}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x91}, | 
 | 	{0x4e, 0x01}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x70}, | 
 | 	{0x4e, 0x01}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x90}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xb0}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x8f}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x6f}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xaf}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xd0}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xf0}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xcf}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xef}, | 
 | 	{0x4e, 0x02}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x6e}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x8e}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xae}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xce}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x4d}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x6d}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x8d}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xad}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xcd}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x4c}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x6c}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x8c}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xac}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xcc}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xcb}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x4b}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x6b}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x8b}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xab}, | 
 | 	{0x4e, 0x03}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x8a}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xaa}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xca}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xca}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xc9}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x8a}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0x89}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xa9}, | 
 | 	{0x4e, 0x04}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x0b}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x0a}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xeb}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x01}, | 
 | 	{0x4d, 0xea}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x09}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x29}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x2a}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x4a}, | 
 | 	{0x4e, 0x05}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x8a}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x49}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x69}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x89}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xa9}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x48}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x68}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0x69}, | 
 | 	{0x4e, 0x06}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xca}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xc9}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xe9}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x03}, | 
 | 	{0x4d, 0x09}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xc8}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xe8}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xa7}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xc7}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x02}, | 
 | 	{0x4d, 0xe7}, | 
 | 	{0x4e, 0x07}, | 
 | 	{0x4c, 0x03}, | 
 | 	{0x4d, 0x07}, | 
 | 	{0x4e, 0x07}, | 
 |  | 
 | 	{0x4f, 0x01}, | 
 | 	{0x50, 0x80}, | 
 | 	{0x51, 0xa8}, | 
 | 	{0x52, 0x47}, | 
 | 	{0x53, 0x38}, | 
 | 	{0x54, 0xc7}, | 
 | 	{0x56, 0x0e}, | 
 | 	{0x58, 0x08}, | 
 | 	{0x5b, 0x00}, | 
 | 	{0x5c, 0x74}, | 
 | 	{0x5d, 0x8b}, | 
 | 	{0x61, 0xdb}, | 
 | 	{0x62, 0xb8}, | 
 | 	{0x63, 0x86}, | 
 | 	{0x64, 0xc0}, | 
 | 	{0x65, 0x04}, | 
 | 	{0x67, 0xa8}, | 
 | 	{0x68, 0xb0}, | 
 | 	{0x69, 0x00}, | 
 | 	{0x6a, 0xa8}, | 
 | 	{0x6b, 0xb0}, | 
 | 	{0x6c, 0xaf}, | 
 | 	{0x6d, 0x8b}, | 
 | 	{0x6e, 0x50}, | 
 | 	{0x6f, 0x18}, | 
 | 	{0x73, 0xf0}, | 
 | 	{0x70, 0x0d}, | 
 | 	{0x71, 0x60}, | 
 | 	{0x72, 0x80}, | 
 | 	{0x74, 0x01}, | 
 | 	{0x75, 0x01}, | 
 | 	{0x7f, 0x0c}, | 
 | 	{0x76, 0x70}, | 
 | 	{0x77, 0x58}, | 
 | 	{0x78, 0xa0}, | 
 | 	{0x79, 0x5e}, | 
 | 	{0x7a, 0x54}, | 
 | 	{0x7b, 0x58}, | 
 | 	{0xfe, 0x00}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0xc0, 0x01}, | 
 | 	{0xc1, 0x44}, | 
 | 	{0xc2, 0xfd}, | 
 | 	{0xc3, 0x04}, | 
 | 	{0xc4, 0xF0}, | 
 | 	{0xc5, 0x48}, | 
 | 	{0xc6, 0xfd}, | 
 | 	{0xc7, 0x46}, | 
 | 	{0xc8, 0xfd}, | 
 | 	{0xc9, 0x02}, | 
 | 	{0xca, 0xe0}, | 
 | 	{0xcb, 0x45}, | 
 | 	{0xcc, 0xec}, | 
 | 	{0xcd, 0x48}, | 
 | 	{0xce, 0xf0}, | 
 | 	{0xcf, 0xf0}, | 
 | 	{0xe3, 0x0c}, | 
 | 	{0xe4, 0x4b}, | 
 | 	{0xe5, 0xe0}, | 
 | 	{0xfe, 0x01}, | 
 | 	{0x9f, 0x40}, | 
 | 	{0xfe, 0x00}, | 
 |  | 
 | 	/* Output Control  */ | 
 | 	{0xfe, 0x00}, | 
 | 	{0xf2, 0x0f}, | 
 | 	{0xfe, 0x02}, | 
 | 	{0x40, 0xbf}, | 
 | 	{0x46, 0xcf}, | 
 | 	{0xfe, 0x00}, | 
 |  | 
 | 	{0xfe, 0x00}, | 
 | 	{0x05, 0x01}, | 
 | 	{0x06, 0x1C}, | 
 | 	{0x07, 0x00}, | 
 | 	{0x08, 0x32}, | 
 | 	{0x11, 0x00}, | 
 | 	{0x12, 0x1D}, | 
 | 	{0x13, 0x00}, | 
 | 	{0x14, 0x00}, | 
 |  | 
 | 	{0xfe, 0x01}, | 
 | 	{0x3c, 0x00}, | 
 | 	{0x3d, 0x04}, | 
 | 	{0xfe, 0x00}, | 
 | 	{0x00, 0x00}, | 
 | }; | 
 |  | 
 | struct gc2145_config { | 
 | 	struct i2c_dt_spec i2c; | 
 | #if DT_INST_NODE_HAS_PROP(0, pwdn_gpios) | 
 | 	struct gpio_dt_spec pwdn_gpio; | 
 | #endif | 
 | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
 | 	struct gpio_dt_spec reset_gpio; | 
 | #endif | 
 | }; | 
 |  | 
 | struct gc2145_data { | 
 | 	struct video_format fmt; | 
 | }; | 
 |  | 
 | #define GC2145_VIDEO_FORMAT_CAP(width, height, format)                                             \ | 
 | 	{                                                                                          \ | 
 | 		.pixelformat = format, .width_min = width, .width_max = width,                     \ | 
 | 		.height_min = height, .height_max = height, .width_step = 0, .height_step = 0,     \ | 
 | 	} | 
 |  | 
 | #define RESOLUTION_QVGA_W	320 | 
 | #define RESOLUTION_QVGA_H	240 | 
 |  | 
 | #define RESOLUTION_VGA_W	640 | 
 | #define RESOLUTION_VGA_H	480 | 
 |  | 
 | #define RESOLUTION_UXGA_W	1600 | 
 | #define RESOLUTION_UXGA_H	1200 | 
 |  | 
 | static const struct video_format_cap fmts[] = { | 
 | 	GC2145_VIDEO_FORMAT_CAP(RESOLUTION_QVGA_W, RESOLUTION_QVGA_H, VIDEO_PIX_FMT_RGB565), | 
 | 	GC2145_VIDEO_FORMAT_CAP(RESOLUTION_VGA_W, RESOLUTION_VGA_H, VIDEO_PIX_FMT_RGB565), | 
 | 	GC2145_VIDEO_FORMAT_CAP(RESOLUTION_UXGA_W, RESOLUTION_UXGA_H, VIDEO_PIX_FMT_RGB565), | 
 | 	GC2145_VIDEO_FORMAT_CAP(RESOLUTION_QVGA_W, RESOLUTION_QVGA_H, VIDEO_PIX_FMT_YUYV), | 
 | 	GC2145_VIDEO_FORMAT_CAP(RESOLUTION_VGA_W, RESOLUTION_VGA_H, VIDEO_PIX_FMT_YUYV), | 
 | 	GC2145_VIDEO_FORMAT_CAP(RESOLUTION_UXGA_W, RESOLUTION_UXGA_H, VIDEO_PIX_FMT_YUYV), | 
 | 	{0}, | 
 | }; | 
 |  | 
 | static int gc2145_write_reg(const struct i2c_dt_spec *spec, uint8_t reg_addr, uint8_t value) | 
 | { | 
 | 	int ret; | 
 | 	uint8_t tries = 3; | 
 |  | 
 | 	/* | 
 | 	 * It rarely happens that the camera does not respond with ACK signal. | 
 | 	 * In that case it usually responds on 2nd try but there is a 3rd one | 
 | 	 * just to be sure that the connection error is not caused by driver | 
 | 	 * itself. | 
 | 	 */ | 
 | 	do { | 
 | 		ret = i2c_reg_write_byte_dt(spec, reg_addr, value); | 
 | 		if (!ret) { | 
 | 			return 0; | 
 | 		} | 
 | 		/* If writing failed wait 5ms before next attempt */ | 
 | 		k_msleep(5); | 
 | 	} while (tries-- > 0); | 
 |  | 
 | 	LOG_ERR("failed to write 0x%x to 0x%x,", value, reg_addr); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int gc2145_read_reg(const struct i2c_dt_spec *spec, uint8_t reg_addr, uint8_t *value) | 
 | { | 
 | 	int ret; | 
 | 	uint8_t tries = 3; | 
 |  | 
 | 	/* | 
 | 	 * It rarely happens that the camera does not respond with ACK signal. | 
 | 	 * In that case it usually responds on 2nd try but there is a 3rd one | 
 | 	 * just to be sure that the connection error is not caused by driver | 
 | 	 * itself. | 
 | 	 */ | 
 | 	do { | 
 | 		ret = i2c_reg_read_byte_dt(spec, reg_addr, value); | 
 | 		if (!ret) { | 
 | 			return 0; | 
 | 		} | 
 | 		/* If writing failed wait 5ms before next attempt */ | 
 | 		k_msleep(5); | 
 | 	} while (tries-- > 0); | 
 |  | 
 | 	LOG_ERR("failed to read 0x%x register", reg_addr); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int gc2145_write_all(const struct device *dev, const struct gc2145_reg *regs, | 
 | 			    uint16_t reg_num) | 
 | { | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 |  | 
 | 	for (uint16_t i = 0; i < reg_num; i++) { | 
 | 		int ret; | 
 |  | 
 | 		ret = gc2145_write_reg(&cfg->i2c, regs[i].addr, regs[i].value); | 
 | 		if (ret < 0) { | 
 | 			return ret; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int gc2145_soft_reset(const struct device *dev) | 
 | { | 
 | 	int ret; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 |  | 
 | 	/* Initiate system reset */ | 
 | 	ret = gc2145_write_reg(&cfg->i2c, GC2145_REG_RESET, GC2145_REG_SW_RESET); | 
 |  | 
 | 	k_msleep(300); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int gc2145_set_ctrl_vflip(const struct device *dev, bool enable) | 
 | { | 
 | 	int ret; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 | 	uint8_t old_value; | 
 |  | 
 | 	ret = gc2145_read_reg(&cfg->i2c, GC2145_REG_AMODE1, &old_value); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Set the vertical flip state */ | 
 | 	return gc2145_write_reg(&cfg->i2c, GC2145_REG_AMODE1, | 
 | 				(old_value & GC2145_AMODE1_WINDOW_MASK) | (enable << 1)); | 
 | } | 
 |  | 
 | static int gc2145_set_ctrl_hmirror(const struct device *dev, bool enable) | 
 | { | 
 | 	int ret; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 | 	uint8_t old_value; | 
 |  | 
 | 	ret = gc2145_read_reg(&cfg->i2c, GC2145_REG_AMODE1, &old_value); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Set the horizontal mirror state */ | 
 | 	return gc2145_write_reg(&cfg->i2c, GC2145_REG_AMODE1, | 
 | 				(old_value & GC2145_AMODE1_WINDOW_MASK) | enable); | 
 | } | 
 |  | 
 | static int gc2145_set_window(const struct device *dev, uint16_t reg, uint16_t x, uint16_t y, | 
 | 			     uint16_t w, uint16_t h) | 
 | { | 
 | 	int ret; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, GC2145_REG_RESET, GC2145_SET_P0_REGS); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Y/row offset */ | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, y >> 8); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, y & 0xff); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* X/col offset */ | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, x >> 8); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, x & 0xff); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Window height */ | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, h >> 8); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, h & 0xff); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Window width */ | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, w >> 8); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, reg++, w & 0xff); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int gc2145_set_output_format(const struct device *dev, int output_format) | 
 | { | 
 | 	int ret; | 
 | 	uint8_t old_value; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, GC2145_REG_RESET, GC2145_SET_P0_REGS); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Map format to sensor format */ | 
 | 	if (output_format == VIDEO_PIX_FMT_RGB565) { | 
 | 		output_format = GC2145_REG_OUTPUT_FMT_RGB565; | 
 | 	} else if (output_format == VIDEO_PIX_FMT_YUYV) { | 
 | 		output_format = GC2145_REG_OUTPUT_FMT_YCBYCR; | 
 | 	} else { | 
 | 		LOG_ERR("Image format not supported"); | 
 | 		return -ENOTSUP; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_read_reg(&cfg->i2c, GC2145_REG_OUTPUT_FMT, &old_value); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, GC2145_REG_OUTPUT_FMT, | 
 | 			(old_value & ~GC2145_REG_OUTPUT_FMT_MASK) | output_format); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	k_sleep(K_MSEC(30)); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int gc2145_set_resolution(const struct device *dev, uint32_t w, uint32_t h) | 
 | { | 
 | 	int ret; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 |  | 
 | 	uint16_t win_w; | 
 | 	uint16_t win_h; | 
 | 	uint16_t c_ratio; | 
 | 	uint16_t r_ratio; | 
 | 	uint16_t x; | 
 | 	uint16_t y; | 
 | 	uint16_t win_x; | 
 | 	uint16_t win_y; | 
 |  | 
 | 	/* Add the subsampling factor depending on resolution */ | 
 | 	switch (w) { | 
 | 	case RESOLUTION_QVGA_W: | 
 | 		c_ratio = 3; | 
 | 		r_ratio = 3; | 
 | 		break; | 
 | 	case RESOLUTION_VGA_W: | 
 | 		c_ratio = 2; | 
 | 		r_ratio = 2; | 
 | 		break; | 
 | 	case RESOLUTION_UXGA_W: | 
 | 		c_ratio = 1; | 
 | 		r_ratio = 1; | 
 | 		break; | 
 | 	default: | 
 | 		LOG_ERR("Unsupported resolution %d %d", w, h); | 
 | 		return -EIO; | 
 | 	}; | 
 |  | 
 | 	/* Calculates the window boundaries to obtain the desired resolution */ | 
 | 	win_w = w * c_ratio; | 
 | 	win_h = h * r_ratio; | 
 | 	x = (((win_w / c_ratio) - w) / 2); | 
 | 	y = (((win_h / r_ratio) - h) / 2); | 
 | 	win_x = ((UXGA_HSIZE - win_w) / 2); | 
 | 	win_y = ((UXGA_VSIZE - win_h) / 2); | 
 |  | 
 | 	/* Set readout window first. */ | 
 | 	ret = gc2145_set_window(dev, GC2145_REG_BLANK_WINDOW_BASE, win_x, win_y, win_w + 16, | 
 | 				win_h + 8); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Set cropping window next. */ | 
 | 	ret = gc2145_set_window(dev, GC2145_REG_WINDOW_BASE, x, y, w, h); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Enable crop */ | 
 | 	ret = gc2145_write_reg(&cfg->i2c, GC2145_REG_CROP_ENABLE, GC2145_CROP_SET_ENABLE); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Set Sub-sampling ratio and mode */ | 
 | 	ret = gc2145_write_reg(&cfg->i2c, GC2145_REG_SUBSAMPLE, ((r_ratio << 4) | c_ratio)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_write_reg(&cfg->i2c, GC2145_REG_SUBSAMPLE_MODE, GC2145_SUBSAMPLE_MODE_SMOOTH); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 | 	/* | 
 | 	 * Galaxy Core datasheet does not document the reason behind of this | 
 | 	 * and other short delay requirements, but the reason exposed by them | 
 | 	 * is to give enough time for the sensor DSP to handle the I2C transaction | 
 | 	 * give some time time to apply the changes before the next instruction. | 
 | 	 */ | 
 | 	k_sleep(K_MSEC(30)); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static uint8_t gc2145_check_connection(const struct device *dev) | 
 | { | 
 | 	int ret; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 | 	uint8_t reg_pid_val; | 
 | 	uint8_t reg_ver_val; | 
 |  | 
 | 	ret = gc2145_read_reg(&cfg->i2c, 0xf0, ®_pid_val); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = gc2145_read_reg(&cfg->i2c, 0xf1, ®_ver_val); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	if ((reg_ver_val != GC2145_REV_VAL) || (reg_pid_val != GC2145_PID_VAL)) { | 
 | 		LOG_WRN("Unexpected GC2145 pid: 0x%x or rev: 0x%x", reg_pid_val, reg_ver_val); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int gc2145_set_fmt(const struct device *dev, enum video_endpoint_id ep, | 
 | 			  struct video_format *fmt) | 
 | { | 
 | 	struct gc2145_data *drv_data = dev->data; | 
 | 	size_t res = ARRAY_SIZE(fmts); | 
 | 	int ret; | 
 |  | 
 | 	if (memcmp(&drv_data->fmt, fmt, sizeof(drv_data->fmt)) == 0) { | 
 | 		/* nothing to do */ | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	/* Check if camera is capable of handling given format */ | 
 | 	for (int i = 0; i < ARRAY_SIZE(fmts) - 1; i++) { | 
 | 		if (fmts[i].width_min == fmt->width && fmts[i].height_min == fmt->height && | 
 | 		    fmts[i].pixelformat == fmt->pixelformat) { | 
 | 			res = i; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	if (res == ARRAY_SIZE(fmts)) { | 
 | 		LOG_ERR("Image format not supported"); | 
 | 		return -ENOTSUP; | 
 | 	} | 
 |  | 
 | 	drv_data->fmt = *fmt; | 
 |  | 
 | 	/* Set output format */ | 
 | 	ret = gc2145_set_output_format(dev, fmt->pixelformat); | 
 | 	if (ret < 0) { | 
 | 		LOG_ERR("Failed to set the output format"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* Set window size */ | 
 | 	ret = gc2145_set_resolution(dev, fmt->width, fmt->height); | 
 |  | 
 | 	if (ret < 0) { | 
 | 		LOG_ERR("Failed to set the resolution"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int gc2145_get_fmt(const struct device *dev, enum video_endpoint_id ep, | 
 | 			  struct video_format *fmt) | 
 | { | 
 | 	struct gc2145_data *drv_data = dev->data; | 
 |  | 
 | 	*fmt = drv_data->fmt; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int gc2145_set_stream(const struct device *dev, bool enable) | 
 | { | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 |  | 
 | 	return enable ? gc2145_write_reg(&cfg->i2c, 0xf2, 0x0f) | 
 | 		      : gc2145_write_reg(&cfg->i2c, 0xf2, 0x00); | 
 | } | 
 |  | 
 | static int gc2145_get_caps(const struct device *dev, enum video_endpoint_id ep, | 
 | 			   struct video_caps *caps) | 
 | { | 
 | 	caps->format_caps = fmts; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int gc2145_set_ctrl(const struct device *dev, unsigned int cid, void *value) | 
 | { | 
 | 	switch (cid) { | 
 | 	case VIDEO_CID_HFLIP: | 
 | 		return gc2145_set_ctrl_hmirror(dev, (int)value); | 
 | 	case VIDEO_CID_VFLIP: | 
 | 		return gc2145_set_ctrl_vflip(dev, (int)value); | 
 | 	default: | 
 | 		return -ENOTSUP; | 
 | 	} | 
 | } | 
 |  | 
 | static DEVICE_API(video, gc2145_driver_api) = { | 
 | 	.set_format = gc2145_set_fmt, | 
 | 	.get_format = gc2145_get_fmt, | 
 | 	.get_caps = gc2145_get_caps, | 
 | 	.set_stream = gc2145_set_stream, | 
 | 	.set_ctrl = gc2145_set_ctrl, | 
 | }; | 
 |  | 
 | static int gc2145_init(const struct device *dev) | 
 | { | 
 | 	struct video_format fmt; | 
 | 	int ret; | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 | 	(void) cfg; | 
 |  | 
 | #if DT_INST_NODE_HAS_PROP(0, pwdn_gpios) | 
 | 	ret = gpio_pin_configure_dt(&cfg->pwdn_gpio, GPIO_OUTPUT_INACTIVE); | 
 | 	if (ret) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	k_sleep(K_MSEC(10)); | 
 | #endif | 
 | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
 | 	ret = gpio_pin_configure_dt(&cfg->reset_gpio, GPIO_OUTPUT_ACTIVE); | 
 | 	if (ret) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	k_sleep(K_MSEC(1)); | 
 | 	gpio_pin_set_dt(&cfg->reset_gpio, 0); | 
 | 	k_sleep(K_MSEC(1)); | 
 | #endif | 
 |  | 
 | 	ret = gc2145_check_connection(dev); | 
 | 	if (ret) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	gc2145_soft_reset(dev); | 
 | 	gc2145_write_all(dev, default_regs, ARRAY_SIZE(default_regs)); | 
 |  | 
 | 	/* set default/init format QVGA RGB565 */ | 
 | 	fmt.pixelformat = VIDEO_PIX_FMT_RGB565; | 
 | 	fmt.width = RESOLUTION_QVGA_W; | 
 | 	fmt.height = RESOLUTION_QVGA_H; | 
 | 	fmt.pitch = RESOLUTION_QVGA_W * 2; | 
 |  | 
 | 	ret = gc2145_set_fmt(dev, VIDEO_EP_OUT, &fmt); | 
 | 	if (ret) { | 
 | 		LOG_ERR("Unable to configure default format"); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Unique Instance */ | 
 | static const struct gc2145_config gc2145_cfg_0 = { | 
 | 	.i2c = I2C_DT_SPEC_INST_GET(0), | 
 | #if DT_INST_NODE_HAS_PROP(0, pwdn_gpios) | 
 | 	.pwdn_gpio = GPIO_DT_SPEC_INST_GET(0, pwdn_gpios), | 
 | #endif | 
 | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
 | 	.reset_gpio = GPIO_DT_SPEC_INST_GET(0, reset_gpios), | 
 | #endif | 
 | }; | 
 | static struct gc2145_data gc2145_data_0; | 
 |  | 
 | static int gc2145_init_0(const struct device *dev) | 
 | { | 
 | 	const struct gc2145_config *cfg = dev->config; | 
 |  | 
 | 	if (!i2c_is_ready_dt(&cfg->i2c)) { | 
 | 		LOG_ERR("Bus device is not ready"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | #if DT_INST_NODE_HAS_PROP(0, pwdn_gpios) | 
 | 	if (!gpio_is_ready_dt(&cfg->pwdn_gpio)) { | 
 | 		LOG_ERR("%s: device %s is not ready", dev->name, cfg->pwdn_gpio.port->name); | 
 | 		return -ENODEV; | 
 | 	} | 
 | #endif | 
 | #if DT_INST_NODE_HAS_PROP(0, reset_gpios) | 
 | 	if (!gpio_is_ready_dt(&cfg->reset_gpio)) { | 
 | 		LOG_ERR("%s: device %s is not ready", dev->name, cfg->reset_gpio.port->name); | 
 | 		return -ENODEV; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	return gc2145_init(dev); | 
 | } | 
 |  | 
 | DEVICE_DT_INST_DEFINE(0, &gc2145_init_0, NULL, &gc2145_data_0, &gc2145_cfg_0, POST_KERNEL, | 
 | 		      CONFIG_VIDEO_INIT_PRIORITY, &gc2145_driver_api); |