ll_cam.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Copyright 2010-2020 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include "soc/system_reg.h"
  17. #include "soc/lcd_cam_struct.h"
  18. #include "soc/lcd_cam_reg.h"
  19. #include "soc/gdma_struct.h"
  20. #include "soc/gdma_periph.h"
  21. #include "soc/gdma_reg.h"
  22. #include "ll_cam.h"
  23. #include "cam_hal.h"
  24. #if (ESP_IDF_VERSION_MAJOR >= 5)
  25. #define gpio_matrix_in(a,b,c) gpio_iomux_in(a,b)
  26. #define gpio_matrix_out(a,b,c,d) gpio_iomux_out(a,b,c)
  27. #endif
  28. static const char *TAG = "s3 ll_cam";
  29. static void IRAM_ATTR ll_cam_vsync_isr(void *arg)
  30. {
  31. //DBG_PIN_SET(1);
  32. cam_obj_t *cam = (cam_obj_t *)arg;
  33. BaseType_t HPTaskAwoken = pdFALSE;
  34. typeof(LCD_CAM.lc_dma_int_st) status = LCD_CAM.lc_dma_int_st;
  35. if (status.val == 0) {
  36. return;
  37. }
  38. LCD_CAM.lc_dma_int_clr.val = status.val;
  39. if (status.cam_vsync_int_st) {
  40. ll_cam_send_event(cam, CAM_VSYNC_EVENT, &HPTaskAwoken);
  41. }
  42. if (HPTaskAwoken == pdTRUE) {
  43. portYIELD_FROM_ISR();
  44. }
  45. //DBG_PIN_SET(0);
  46. }
  47. static void IRAM_ATTR ll_cam_dma_isr(void *arg)
  48. {
  49. cam_obj_t *cam = (cam_obj_t *)arg;
  50. BaseType_t HPTaskAwoken = pdFALSE;
  51. typeof(GDMA.channel[cam->dma_num].in.int_st) status = GDMA.channel[cam->dma_num].in.int_st;
  52. if (status.val == 0) {
  53. return;
  54. }
  55. GDMA.channel[cam->dma_num].in.int_clr.val = status.val;
  56. if (status.in_suc_eof) {
  57. ll_cam_send_event(cam, CAM_IN_SUC_EOF_EVENT, &HPTaskAwoken);
  58. }
  59. if (HPTaskAwoken == pdTRUE) {
  60. portYIELD_FROM_ISR();
  61. }
  62. }
  63. bool ll_cam_stop(cam_obj_t *cam)
  64. {
  65. if (cam->jpeg_mode || !cam->psram_mode) {
  66. GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 0;
  67. GDMA.channel[cam->dma_num].in.int_clr.in_suc_eof = 1;
  68. }
  69. GDMA.channel[cam->dma_num].in.link.stop = 1;
  70. return true;
  71. }
  72. esp_err_t ll_cam_deinit(cam_obj_t *cam)
  73. {
  74. if (cam->cam_intr_handle) {
  75. esp_intr_free(cam->cam_intr_handle);
  76. cam->cam_intr_handle = NULL;
  77. }
  78. if (cam->dma_intr_handle) {
  79. esp_intr_free(cam->dma_intr_handle);
  80. cam->dma_intr_handle = NULL;
  81. }
  82. GDMA.channel[cam->dma_num].in.link.addr = 0x0;
  83. LCD_CAM.cam_ctrl1.cam_start = 0;
  84. LCD_CAM.cam_ctrl1.cam_reset = 1;
  85. LCD_CAM.cam_ctrl1.cam_reset = 0;
  86. return ESP_OK;
  87. }
  88. bool ll_cam_start(cam_obj_t *cam, int frame_pos)
  89. {
  90. LCD_CAM.cam_ctrl1.cam_start = 0;
  91. if (cam->jpeg_mode || !cam->psram_mode) {
  92. GDMA.channel[cam->dma_num].in.int_clr.in_suc_eof = 1;
  93. GDMA.channel[cam->dma_num].in.int_ena.in_suc_eof = 1;
  94. }
  95. LCD_CAM.cam_ctrl1.cam_reset = 1;
  96. LCD_CAM.cam_ctrl1.cam_reset = 0;
  97. LCD_CAM.cam_ctrl1.cam_afifo_reset = 1;
  98. LCD_CAM.cam_ctrl1.cam_afifo_reset = 0;
  99. GDMA.channel[cam->dma_num].in.conf0.in_rst = 1;
  100. GDMA.channel[cam->dma_num].in.conf0.in_rst = 0;
  101. LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = cam->dma_half_buffer_size - 1; // Ping pong operation
  102. if (!cam->psram_mode) {
  103. GDMA.channel[cam->dma_num].in.link.addr = ((uint32_t)&cam->dma[0]) & 0xfffff;
  104. } else {
  105. GDMA.channel[cam->dma_num].in.link.addr = ((uint32_t)&cam->frames[frame_pos].dma[0]) & 0xfffff;
  106. }
  107. GDMA.channel[cam->dma_num].in.link.start = 1;
  108. LCD_CAM.cam_ctrl.cam_update = 1;
  109. LCD_CAM.cam_ctrl1.cam_start = 1;
  110. return true;
  111. }
  112. static esp_err_t ll_cam_dma_init(cam_obj_t *cam)
  113. {
  114. for (int x = (SOC_GDMA_PAIRS_PER_GROUP - 1); x >= 0; x--) {
  115. if (GDMA.channel[x].in.link.addr == 0x0) {
  116. cam->dma_num = x;
  117. ESP_LOGI(TAG, "DMA Channel=%d", cam->dma_num);
  118. break;
  119. }
  120. if (x == 0) {
  121. cam_deinit();
  122. ESP_LOGE(TAG, "Can't found available GDMA channel");
  123. return ESP_FAIL;
  124. }
  125. }
  126. if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN) == 0) {
  127. REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
  128. REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_DMA_CLK_EN);
  129. REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
  130. REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST);
  131. }
  132. GDMA.channel[cam->dma_num].in.int_clr.val = ~0;
  133. GDMA.channel[cam->dma_num].in.int_ena.val = 0;
  134. GDMA.channel[cam->dma_num].in.conf0.val = 0;
  135. GDMA.channel[cam->dma_num].in.conf0.in_rst = 1;
  136. GDMA.channel[cam->dma_num].in.conf0.in_rst = 0;
  137. //internal SRAM only
  138. if (!cam->psram_mode) {
  139. GDMA.channel[cam->dma_num].in.conf0.indscr_burst_en = 1;
  140. GDMA.channel[cam->dma_num].in.conf0.in_data_burst_en = 1;
  141. }
  142. GDMA.channel[cam->dma_num].in.conf1.in_check_owner = 0;
  143. GDMA.channel[cam->dma_num].in.peri_sel.sel = 5;
  144. //GDMA.channel[cam->dma_num].in.pri.rx_pri = 1;//rx prio 0-15
  145. //GDMA.channel[cam->dma_num].in.sram_size.in_size = 6;//This register is used to configure the size of L2 Tx FIFO for Rx channel. 0:16 bytes, 1:24 bytes, 2:32 bytes, 3: 40 bytes, 4: 48 bytes, 5:56 bytes, 6: 64 bytes, 7: 72 bytes, 8: 80 bytes.
  146. //GDMA.channel[cam->dma_num].in.wight.rx_weight = 7;//The weight of Rx channel 0-15
  147. return ESP_OK;
  148. }
  149. esp_err_t ll_cam_config(cam_obj_t *cam, const camera_config_t *config)
  150. {
  151. if (REG_GET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN) == 0) {
  152. REG_CLR_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
  153. REG_SET_BIT(SYSTEM_PERIP_CLK_EN1_REG, SYSTEM_LCD_CAM_CLK_EN);
  154. REG_SET_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
  155. REG_CLR_BIT(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_LCD_CAM_RST);
  156. }
  157. LCD_CAM.cam_ctrl.val = 0;
  158. LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
  159. LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
  160. LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / config->xclk_freq_hz;
  161. LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.
  162. LCD_CAM.cam_ctrl.cam_stop_en = 0;
  163. LCD_CAM.cam_ctrl.cam_vsync_filter_thres = 4; // Filter by LCD_CAM clock
  164. LCD_CAM.cam_ctrl.cam_update = 0;
  165. LCD_CAM.cam_ctrl.cam_byte_order = cam->swap_data;
  166. LCD_CAM.cam_ctrl.cam_bit_order = 0;
  167. LCD_CAM.cam_ctrl.cam_line_int_en = 0;
  168. LCD_CAM.cam_ctrl.cam_vs_eof_en = 0; //1: CAM_VSYNC to generate in_suc_eof. 0: in_suc_eof is controlled by reg_cam_rec_data_cyclelen
  169. LCD_CAM.cam_ctrl1.val = 0;
  170. LCD_CAM.cam_ctrl1.cam_rec_data_bytelen = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE - 1; // Cannot be assigned to 0, and it is easy to overflow
  171. LCD_CAM.cam_ctrl1.cam_line_int_num = 0; // The number of hsyncs that generate hs interrupts
  172. LCD_CAM.cam_ctrl1.cam_clk_inv = 0;
  173. LCD_CAM.cam_ctrl1.cam_vsync_filter_en = 1;
  174. LCD_CAM.cam_ctrl1.cam_2byte_en = 0;
  175. LCD_CAM.cam_ctrl1.cam_de_inv = 0;
  176. LCD_CAM.cam_ctrl1.cam_hsync_inv = 0;
  177. LCD_CAM.cam_ctrl1.cam_vsync_inv = 0;
  178. LCD_CAM.cam_ctrl1.cam_vh_de_mode_en = 0;
  179. LCD_CAM.cam_rgb_yuv.val = 0;
  180. LCD_CAM.cam_ctrl.cam_update = 1;
  181. LCD_CAM.cam_ctrl1.cam_start = 1;
  182. esp_err_t err = ll_cam_dma_init(cam);
  183. if(err != ESP_OK) {
  184. return err;
  185. }
  186. return ESP_OK;
  187. }
  188. void ll_cam_vsync_intr_enable(cam_obj_t *cam, bool en)
  189. {
  190. LCD_CAM.lc_dma_int_clr.cam_vsync_int_clr = 1;
  191. if (en) {
  192. LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 1;
  193. } else {
  194. LCD_CAM.lc_dma_int_ena.cam_vsync_int_ena = 0;
  195. }
  196. }
  197. esp_err_t ll_cam_set_pin(cam_obj_t *cam, const camera_config_t *config)
  198. {
  199. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_pclk], PIN_FUNC_GPIO);
  200. gpio_set_direction(config->pin_pclk, GPIO_MODE_INPUT);
  201. gpio_set_pull_mode(config->pin_pclk, GPIO_FLOATING);
  202. gpio_matrix_in(config->pin_pclk, CAM_PCLK_IDX, false);
  203. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_vsync], PIN_FUNC_GPIO);
  204. gpio_set_direction(config->pin_vsync, GPIO_MODE_INPUT);
  205. gpio_set_pull_mode(config->pin_vsync, GPIO_FLOATING);
  206. gpio_matrix_in(config->pin_vsync, CAM_V_SYNC_IDX, cam->vsync_invert);
  207. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_href], PIN_FUNC_GPIO);
  208. gpio_set_direction(config->pin_href, GPIO_MODE_INPUT);
  209. gpio_set_pull_mode(config->pin_href, GPIO_FLOATING);
  210. gpio_matrix_in(config->pin_href, CAM_H_ENABLE_IDX, false);
  211. int data_pins[8] = {
  212. config->pin_d0, config->pin_d1, config->pin_d2, config->pin_d3, config->pin_d4, config->pin_d5, config->pin_d6, config->pin_d7,
  213. };
  214. for (int i = 0; i < 8; i++) {
  215. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[data_pins[i]], PIN_FUNC_GPIO);
  216. gpio_set_direction(data_pins[i], GPIO_MODE_INPUT);
  217. gpio_set_pull_mode(data_pins[i], GPIO_FLOATING);
  218. gpio_matrix_in(data_pins[i], CAM_DATA_IN0_IDX + i, false);
  219. }
  220. PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[config->pin_xclk], PIN_FUNC_GPIO);
  221. gpio_set_direction(config->pin_xclk, GPIO_MODE_OUTPUT);
  222. gpio_set_pull_mode(config->pin_xclk, GPIO_FLOATING);
  223. gpio_matrix_out(config->pin_xclk, CAM_CLK_IDX, false, false);
  224. return ESP_OK;
  225. }
  226. esp_err_t ll_cam_init_isr(cam_obj_t *cam)
  227. {
  228. esp_err_t ret = ESP_OK;
  229. ret = esp_intr_alloc_intrstatus(gdma_periph_signals.groups[0].pairs[cam->dma_num].rx_irq_id,
  230. ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM,
  231. (uint32_t)&GDMA.channel[cam->dma_num].in.int_st, GDMA_IN_SUC_EOF_CH0_INT_ST_M,
  232. ll_cam_dma_isr, cam, &cam->dma_intr_handle);
  233. if (ret != ESP_OK) {
  234. ESP_LOGE(TAG, "DMA interrupt allocation of camera failed");
  235. return ret;
  236. }
  237. ret = esp_intr_alloc_intrstatus(ETS_LCD_CAM_INTR_SOURCE,
  238. ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM,
  239. (uint32_t)&LCD_CAM.lc_dma_int_st.val, LCD_CAM_CAM_VSYNC_INT_ST_M,
  240. ll_cam_vsync_isr, cam, &cam->cam_intr_handle);
  241. if (ret != ESP_OK) {
  242. ESP_LOGE(TAG, "LCD_CAM interrupt allocation of camera failed");
  243. return ret;
  244. }
  245. return ESP_OK;
  246. }
  247. void ll_cam_do_vsync(cam_obj_t *cam)
  248. {
  249. gpio_matrix_in(cam->vsync_pin, CAM_V_SYNC_IDX, !cam->vsync_invert);
  250. ets_delay_us(10);
  251. gpio_matrix_in(cam->vsync_pin, CAM_V_SYNC_IDX, cam->vsync_invert);
  252. }
  253. uint8_t ll_cam_get_dma_align(cam_obj_t *cam)
  254. {
  255. return 16 << GDMA.channel[cam->dma_num].in.conf1.in_ext_mem_bk_size;
  256. }
  257. static bool ll_cam_calc_rgb_dma(cam_obj_t *cam){
  258. size_t node_max = LCD_CAM_DMA_NODE_BUFFER_MAX_SIZE / cam->dma_bytes_per_item;
  259. size_t line_width = cam->width * cam->in_bytes_per_pixel;
  260. size_t node_size = node_max;
  261. size_t nodes_per_line = 1;
  262. size_t lines_per_node = 1;
  263. // Calculate DMA Node Size so that it's divisable by or divisor of the line width
  264. if(line_width >= node_max){
  265. // One or more nodes will be requied for one line
  266. for(size_t i = node_max; i > 0; i=i-1){
  267. if ((line_width % i) == 0) {
  268. node_size = i;
  269. nodes_per_line = line_width / node_size;
  270. break;
  271. }
  272. }
  273. } else {
  274. // One or more lines can fit into one node
  275. for(size_t i = node_max; i > 0; i=i-1){
  276. if ((i % line_width) == 0) {
  277. node_size = i;
  278. lines_per_node = node_size / line_width;
  279. while((cam->height % lines_per_node) != 0){
  280. lines_per_node = lines_per_node - 1;
  281. node_size = lines_per_node * line_width;
  282. }
  283. break;
  284. }
  285. }
  286. }
  287. ESP_LOGI(TAG, "node_size: %4u, nodes_per_line: %u, lines_per_node: %u",
  288. node_size * cam->dma_bytes_per_item, nodes_per_line, lines_per_node);
  289. cam->dma_node_buffer_size = node_size * cam->dma_bytes_per_item;
  290. size_t dma_half_buffer_max = CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX / 2 / cam->dma_bytes_per_item;
  291. if (line_width > dma_half_buffer_max) {
  292. ESP_LOGE(TAG, "Resolution too high");
  293. return 0;
  294. }
  295. // Calculate minimum EOF size = max(mode_size, line_size)
  296. size_t dma_half_buffer_min = node_size * nodes_per_line;
  297. // Calculate max EOF size divisable by node size
  298. size_t dma_half_buffer = (dma_half_buffer_max / dma_half_buffer_min) * dma_half_buffer_min;
  299. // Adjust EOF size so that height will be divisable by the number of lines in each EOF
  300. size_t lines_per_half_buffer = dma_half_buffer / line_width;
  301. while((cam->height % lines_per_half_buffer) != 0){
  302. dma_half_buffer = dma_half_buffer - dma_half_buffer_min;
  303. lines_per_half_buffer = dma_half_buffer / line_width;
  304. }
  305. // Calculate DMA size
  306. size_t dma_buffer_max = 2 * dma_half_buffer_max;
  307. if (cam->psram_mode) {
  308. dma_buffer_max = cam->recv_size / cam->dma_bytes_per_item;
  309. }
  310. size_t dma_buffer_size = dma_buffer_max;
  311. if (!cam->psram_mode) {
  312. dma_buffer_size =(dma_buffer_max / dma_half_buffer) * dma_half_buffer;
  313. }
  314. ESP_LOGI(TAG, "dma_half_buffer_min: %5u, dma_half_buffer: %5u, lines_per_half_buffer: %2u, dma_buffer_size: %5u",
  315. dma_half_buffer_min * cam->dma_bytes_per_item, dma_half_buffer * cam->dma_bytes_per_item, lines_per_half_buffer, dma_buffer_size * cam->dma_bytes_per_item);
  316. cam->dma_buffer_size = dma_buffer_size * cam->dma_bytes_per_item;
  317. cam->dma_half_buffer_size = dma_half_buffer * cam->dma_bytes_per_item;
  318. cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
  319. return 1;
  320. }
  321. bool ll_cam_dma_sizes(cam_obj_t *cam)
  322. {
  323. cam->dma_bytes_per_item = 1;
  324. if (cam->jpeg_mode) {
  325. if (cam->psram_mode) {
  326. cam->dma_buffer_size = cam->recv_size;
  327. cam->dma_half_buffer_size = 1024;
  328. cam->dma_half_buffer_cnt = cam->dma_buffer_size / cam->dma_half_buffer_size;
  329. cam->dma_node_buffer_size = cam->dma_half_buffer_size;
  330. } else {
  331. cam->dma_half_buffer_cnt = 16;
  332. cam->dma_buffer_size = cam->dma_half_buffer_cnt * 1024;
  333. cam->dma_half_buffer_size = cam->dma_buffer_size / cam->dma_half_buffer_cnt;
  334. cam->dma_node_buffer_size = cam->dma_half_buffer_size;
  335. }
  336. } else {
  337. return ll_cam_calc_rgb_dma(cam);
  338. }
  339. return 1;
  340. }
  341. size_t IRAM_ATTR ll_cam_memcpy(cam_obj_t *cam, uint8_t *out, const uint8_t *in, size_t len)
  342. {
  343. // YUV to Grayscale
  344. if (cam->in_bytes_per_pixel == 2 && cam->fb_bytes_per_pixel == 1) {
  345. size_t end = len / 8;
  346. for (size_t i = 0; i < end; ++i) {
  347. out[0] = in[0];
  348. out[1] = in[2];
  349. out[2] = in[4];
  350. out[3] = in[6];
  351. out += 4;
  352. in += 8;
  353. }
  354. return len / 2;
  355. }
  356. // just memcpy
  357. memcpy(out, in, len);
  358. return len;
  359. }
  360. esp_err_t ll_cam_set_sample_mode(cam_obj_t *cam, pixformat_t pix_format, uint32_t xclk_freq_hz, uint16_t sensor_pid)
  361. {
  362. if (pix_format == PIXFORMAT_GRAYSCALE) {
  363. if (sensor_pid == OV3660_PID || sensor_pid == OV5640_PID || sensor_pid == NT99141_PID) {
  364. cam->in_bytes_per_pixel = 1; // camera sends Y8
  365. } else {
  366. cam->in_bytes_per_pixel = 2; // camera sends YU/YV
  367. }
  368. cam->fb_bytes_per_pixel = 1; // frame buffer stores Y8
  369. } else if (pix_format == PIXFORMAT_YUV422 || pix_format == PIXFORMAT_RGB565) {
  370. cam->in_bytes_per_pixel = 2; // camera sends YU/YV
  371. cam->fb_bytes_per_pixel = 2; // frame buffer stores YU/YV/RGB565
  372. } else if (pix_format == PIXFORMAT_JPEG) {
  373. cam->in_bytes_per_pixel = 1;
  374. cam->fb_bytes_per_pixel = 1;
  375. } else {
  376. ESP_LOGE(TAG, "Requested format is not supported");
  377. return ESP_ERR_NOT_SUPPORTED;
  378. }
  379. return ESP_OK;
  380. }
  381. // implements function from xclk.c to allow dynamic XCLK change
  382. esp_err_t xclk_timer_conf(int ledc_timer, int xclk_freq_hz)
  383. {
  384. LCD_CAM.cam_ctrl.cam_clkm_div_b = 0;
  385. LCD_CAM.cam_ctrl.cam_clkm_div_a = 0;
  386. LCD_CAM.cam_ctrl.cam_clkm_div_num = 160000000 / xclk_freq_hz;
  387. LCD_CAM.cam_ctrl.cam_clk_sel = 3;//Select Camera module source clock. 0: no clock. 1: APLL. 2: CLK160. 3: no clock.
  388. LCD_CAM.cam_ctrl.cam_update = 1;
  389. return ESP_OK;
  390. }