/* * Copyright (C) 2015 Hiroyuki Ikezoe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #define _LARGEFILE64_SOURCE #include #include #include #include #include #define VFE_DEVICE "/dev/v4l-subdev8" static uint32_t vfe_base = 0; static int device_fd = -1; static bool initialized = false; struct msm_vfe_cfg_cmd2 { uint16_t num_cfg; uint16_t cmd_len; void *cfg_data; void *cfg_cmd; }; enum msm_vfe_reg_cfg_type { VFE_WRITE, VFE_WRITE_MB, VFE_READ, VFE_CFG_MASK, VFE_WRITE_DMI_16BIT, VFE_WRITE_DMI_32BIT, VFE_WRITE_DMI_64BIT, VFE_READ_DMI_16BIT, VFE_READ_DMI_32BIT, VFE_READ_DMI_64BIT, GET_SOC_HW_VER, }; struct msm_vfe_reg_rw_info { uint32_t reg_offset; uint32_t cmd_data_offset; uint32_t len; }; struct msm_vfe_reg_mask_info { uint32_t reg_offset; uint32_t mask; uint32_t val; }; struct msm_vfe_reg_dmi_info { uint32_t hi_tbl_offset; /*Optional*/ uint32_t lo_tbl_offset; /*Required*/ uint32_t len; }; struct msm_vfe_reg_cfg_cmd { union { struct msm_vfe_reg_rw_info rw_info; struct msm_vfe_reg_mask_info mask_info; struct msm_vfe_reg_dmi_info dmi_info; } u; enum msm_vfe_reg_cfg_type cmd_type; }; #define BASE_VIDIOC_PRIVATE 192 #define VIDIOC_MSM_VFE_REG_CFG _IOWR('V', BASE_VIDIOC_PRIVATE, struct msm_vfe_cfg_cmd2) static uint32_t addresses[0x100000]; #define START_OFFSET 0x2ac00000 static bool _read(int fd, uint32_t offset, uint32_t *buffer, uint16_t size) { struct msm_vfe_cfg_cmd2 cmd; struct msm_vfe_reg_cfg_cmd cfg_cmd; cmd.num_cfg = 1; cmd.cfg_cmd = &cfg_cmd; cmd.cfg_data = buffer; cmd.cmd_len = size; cfg_cmd.cmd_type = VFE_READ; cfg_cmd.u.rw_info.len = size; cfg_cmd.u.rw_info.cmd_data_offset = 0; cfg_cmd.u.rw_info.reg_offset = offset; if (ioctl(fd, VIDIOC_MSM_VFE_REG_CFG, &cmd) < 0) { printf("ioctl failed due to %s.\n", strerror(errno)); return false; } return true; } static bool _collect_suspicions(const char *device) { int fd; int i, j; uint32_t previous_address = 0; struct msm_vfe_cfg_cmd2 cmd; struct msm_vfe_reg_cfg_cmd cfg_cmd; uint32_t buffer[256]; fd = open(device, O_RDONLY); if (fd < 0) { printf("Failed to open %s due to %s.\n", device, strerror(errno)); return false; } for (i = 0; i < 0x1000; i++) { if (!_read(fd, START_OFFSET + i * sizeof(buffer), buffer, sizeof(buffer))) { close(fd); return false; } for (j = 0; j < sizeof(buffer) / sizeof(uint32_t); j++) { if ((0xc8000000 <= buffer[j] && buffer[j] <= 0xd0000000) && ((buffer[j] << 20) & 0xfff) == 0 && previous_address != buffer[j]) { previous_address = buffer[j]; addresses[i * sizeof(buffer) / sizeof(uint32_t) + j] = buffer[j]; } } } close(fd); return true; } #define RESOURCE_SIZE 0x2000 static uint32_t _find_vfe_base(int fd) { int i, j; uint32_t previous_address = 0; uint32_t buffer[256]; uint32_t found_address = 0; for (i = 0; i < 0x1000; i++) { if (!_read(fd, START_OFFSET + i * sizeof(buffer), buffer, sizeof(buffer))) { printf("ioctl failed with %s.\n", strerror(errno)); return 0; } for (j = 0; j < sizeof(buffer) / sizeof(uint32_t); j++) { if ((0xc8000000 <= buffer[j] && buffer[j] <= 0xd0000000) && ((buffer[j] << 20) & 0xfff) == 0 && previous_address != buffer[j]) { uint32_t new_offset = i * sizeof(buffer) / sizeof(uint32_t) + j; if (addresses[new_offset + 0x800] == buffer[j] - RESOURCE_SIZE) { found_address = buffer[j]; break; } previous_address = buffer[j]; } } } return found_address; } static int _open_device(const char *device) { int fd; if (device_fd > 0) { return device_fd; } fd = open(device, O_RDONLY); if (fd < 0) { printf("Failed to open %s due to %s.\n", device, strerror(errno)); } return fd; } static bool _initialize(const char *device) { if (initialized) { return true; } if (!_collect_suspicions(device)) { return false; } if (device_fd > 0) { close(device_fd); device_fd = -1; } device_fd = _open_device(device); if (device_fd < 0) { return false; } vfe_base = _find_vfe_base(device_fd); if (!vfe_base) { return false; } initialized = true; return true; } bool msm_vfe_read_values_at_address(unsigned long address, int *value, int size) { if (!initialized && !_initialize(VFE_DEVICE)) { return false; } return _read(device_fd, address - vfe_base, value, size); } bool msm_vfe_read_value_at_address(unsigned long address, int *value) { if (!initialized && !_initialize(VFE_DEVICE)) { return false; } return _read(device_fd, address - vfe_base, value, sizeof(*value)); }