| /* Copyright (c) 2011-2014, 2017-2018, The Linux Foundation. |
| * All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * 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. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/clk.h> |
| #include <linux/gpio.h> |
| #include <linux/regulator/consumer.h> |
| #include <linux/io.h> |
| #include <linux/err.h> |
| #include <soc/qcom/camera2.h> |
| #include <linux/msm-bus.h> |
| #include "msm_camera_io_util.h" |
| |
| #define BUFF_SIZE_128 128 |
| |
| #undef CDBG |
| #define CDBG(fmt, args...) pr_debug(fmt, ##args) |
| |
| void msm_camera_io_w(u32 data, void __iomem *addr) |
| { |
| CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data)); |
| writel_relaxed((data), (addr)); |
| } |
| |
| /* This API is to write a block of data |
| * to same address |
| */ |
| int32_t msm_camera_io_w_block(const u32 *addr, void __iomem *base, |
| u32 len) |
| { |
| int i; |
| |
| if (!addr || !len || !base) |
| return -EINVAL; |
| |
| for (i = 0; i < len; i++) { |
| CDBG("%s: len =%d val=%x base =%pK\n", __func__, |
| len, addr[i], base); |
| writel_relaxed(addr[i], base); |
| } |
| return 0; |
| } |
| |
| /* This API is to write a block of registers |
| * which is like a 2 dimensional array table with |
| * register offset and data |
| */ |
| int32_t msm_camera_io_w_reg_block(const u32 *addr, void __iomem *base, |
| u32 len) |
| { |
| int i; |
| |
| if (!addr || !len || !base) |
| return -EINVAL; |
| |
| for (i = 0; i < len; i = i + 2) { |
| CDBG("%s: len =%d val=%x base =%pK reg=%x\n", __func__, |
| len, addr[i + 1], base, addr[i]); |
| writel_relaxed(addr[i + 1], base + addr[i]); |
| } |
| return 0; |
| } |
| |
| void msm_camera_io_w_mb(u32 data, void __iomem *addr) |
| { |
| CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data)); |
| /* ensure write is done */ |
| wmb(); |
| writel_relaxed((data), (addr)); |
| /* ensure write is done */ |
| wmb(); |
| } |
| |
| int32_t msm_camera_io_w_mb_block(const u32 *addr, void __iomem *base, u32 len) |
| { |
| int i; |
| |
| if (!addr || !len || !base) |
| return -EINVAL; |
| |
| for (i = 0; i < len; i++) { |
| /* ensure write is done */ |
| wmb(); |
| CDBG("%s: len =%d val=%x base =%pK\n", __func__, |
| len, addr[i], base); |
| writel_relaxed(addr[i], base); |
| } |
| /* ensure last write is done */ |
| wmb(); |
| return 0; |
| } |
| |
| u32 msm_camera_io_r(void __iomem *addr) |
| { |
| uint32_t data = readl_relaxed(addr); |
| |
| CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data)); |
| return data; |
| } |
| |
| u32 msm_camera_io_r_mb(void __iomem *addr) |
| { |
| uint32_t data; |
| /* ensure read is done */ |
| rmb(); |
| data = readl_relaxed(addr); |
| /* ensure read is done */ |
| rmb(); |
| CDBG("%s: 0x%pK %08x\n", __func__, (addr), (data)); |
| return data; |
| } |
| |
| static void msm_camera_io_memcpy_toio(void __iomem *dest_addr, |
| void *src_addr, u32 len) |
| { |
| int i; |
| u32 __iomem *d = (u32 __iomem *) dest_addr; |
| u32 *s = (u32 *) src_addr; |
| |
| for (i = 0; i < len; i++) |
| writel_relaxed(*s++, d++); |
| } |
| |
| int32_t msm_camera_io_poll_value(void __iomem *addr, u32 wait_data, u32 retry, |
| unsigned long min_usecs, unsigned long max_usecs) |
| { |
| uint32_t tmp, cnt = 0; |
| int32_t rc = 0; |
| |
| if (!addr) |
| return -EINVAL; |
| |
| tmp = msm_camera_io_r(addr); |
| while ((tmp != wait_data) && (cnt++ < retry)) { |
| if (min_usecs > 0 && max_usecs > 0) |
| usleep_range(min_usecs, max_usecs); |
| tmp = msm_camera_io_r(addr); |
| } |
| if (cnt > retry) { |
| pr_debug("Poll failed by value\n"); |
| rc = -EINVAL; |
| } |
| return rc; |
| } |
| |
| int32_t msm_camera_io_poll_value_wmask(void __iomem *addr, u32 wait_data, |
| u32 bmask, u32 retry, unsigned long min_usecs, unsigned long max_usecs) |
| { |
| uint32_t tmp, cnt = 0; |
| int32_t rc = 0; |
| |
| if (!addr) |
| return -EINVAL; |
| |
| tmp = msm_camera_io_r(addr); |
| while (((tmp & bmask) != wait_data) && (cnt++ < retry)) { |
| if (min_usecs > 0 && max_usecs > 0) |
| usleep_range(min_usecs, max_usecs); |
| tmp = msm_camera_io_r(addr); |
| } |
| if (cnt > retry) { |
| pr_debug("Poll failed with mask\n"); |
| rc = -EINVAL; |
| } |
| return rc; |
| } |
| |
| void msm_camera_io_dump(void __iomem *addr, int size, int enable) |
| { |
| char line_str[128]; |
| int i; |
| ptrdiff_t p = 0; |
| size_t offset = 0, used = 0; |
| u32 data; |
| |
| CDBG("%s: addr=%pK size=%d\n", __func__, addr, size); |
| |
| if (!addr || (size <= 0) || !enable) |
| return; |
| |
| line_str[0] = '\0'; |
| for (i = 0; i < size/4; i++) { |
| if (i % 4 == 0) { |
| used = snprintf(line_str + offset, |
| sizeof(line_str) - offset, "0x%04tX: ", p); |
| if (offset + used >= sizeof(line_str)) { |
| pr_err("%s\n", line_str); |
| offset = 0; |
| line_str[0] = '\0'; |
| } else { |
| offset += used; |
| } |
| } |
| data = readl_relaxed(addr + p); |
| p = p + 4; |
| used = snprintf(line_str + offset, |
| sizeof(line_str) - offset, "%08x ", data); |
| if (offset + used >= sizeof(line_str)) { |
| pr_err("%s\n", line_str); |
| offset = 0; |
| line_str[0] = '\0'; |
| } else { |
| offset += used; |
| } |
| if ((i + 1) % 4 == 0) { |
| pr_err("%s\n", line_str); |
| line_str[0] = '\0'; |
| offset = 0; |
| } |
| } |
| if (line_str[0] != '\0') |
| pr_err("%s\n", line_str); |
| } |
| |
| void msm_camera_io_dump_wstring_base(void __iomem *addr, |
| struct msm_cam_dump_string_info *dump_data, |
| int size) |
| { |
| int i, u = sizeof(struct msm_cam_dump_string_info); |
| |
| pr_debug("%s: addr=%pK data=%pK size=%d u=%d, cnt=%d\n", __func__, |
| addr, dump_data, size, u, |
| (size/u)); |
| |
| if (!addr || (size <= 0) || !dump_data) { |
| pr_err("%s: addr=%pK data=%pK size=%d\n", __func__, |
| addr, dump_data, size); |
| return; |
| } |
| for (i = 0; i < (size / u); i++) |
| pr_debug("%s 0x%x\n", (dump_data + i)->print, |
| readl_relaxed((dump_data + i)->offset + addr)); |
| } |
| |
| void msm_camera_io_memcpy(void __iomem *dest_addr, |
| void *src_addr, u32 len) |
| { |
| CDBG("%s: %pK %pK %d\n", __func__, dest_addr, src_addr, len); |
| msm_camera_io_memcpy_toio(dest_addr, src_addr, len / 4); |
| } |
| |
| void msm_camera_io_memcpy_mb(void __iomem *dest_addr, |
| void *src_addr, u32 len) |
| { |
| int i; |
| u32 __iomem *d = (u32 __iomem *) dest_addr; |
| u32 *s = (u32 *) src_addr; |
| /* This is generic function called who needs to register |
| * writes with memory barrier |
| */ |
| wmb(); |
| for (i = 0; i < (len / 4); i++) { |
| msm_camera_io_w(*s++, d++); |
| /* ensure write is done after every iteration */ |
| wmb(); |
| } |
| } |
| |
| int msm_cam_clk_sel_src(struct device *dev, struct msm_cam_clk_info *clk_info, |
| struct msm_cam_clk_info *clk_src_info, int num_clk) |
| { |
| int i; |
| int rc = 0; |
| struct clk *mux_clk = NULL; |
| struct clk *src_clk = NULL; |
| |
| for (i = 0; i < num_clk; i++) { |
| if (clk_src_info[i].clk_name) { |
| mux_clk = clk_get(dev, clk_info[i].clk_name); |
| if (IS_ERR(mux_clk)) { |
| pr_err("%s get failed\n", |
| clk_info[i].clk_name); |
| continue; |
| } |
| src_clk = clk_get(dev, clk_src_info[i].clk_name); |
| if (IS_ERR(src_clk)) { |
| pr_err("%s get failed\n", |
| clk_src_info[i].clk_name); |
| continue; |
| } |
| clk_set_parent(mux_clk, src_clk); |
| } |
| } |
| return rc; |
| } |
| |
| int msm_cam_clk_enable(struct device *dev, struct msm_cam_clk_info *clk_info, |
| struct clk **clk_ptr, int num_clk, int enable) |
| { |
| int i; |
| int rc = 0; |
| long clk_rate; |
| |
| if (enable) { |
| for (i = 0; i < num_clk; i++) { |
| CDBG("%s enable %s\n", __func__, clk_info[i].clk_name); |
| clk_ptr[i] = clk_get(dev, clk_info[i].clk_name); |
| if (IS_ERR(clk_ptr[i])) { |
| pr_err("%s get failed\n", clk_info[i].clk_name); |
| rc = PTR_ERR(clk_ptr[i]); |
| goto cam_clk_get_err; |
| } |
| if (clk_info[i].clk_rate > 0) { |
| clk_rate = clk_round_rate(clk_ptr[i], |
| clk_info[i].clk_rate); |
| if (clk_rate < 0) { |
| pr_err("%s round failed\n", |
| clk_info[i].clk_name); |
| goto cam_clk_set_err; |
| } |
| rc = clk_set_rate(clk_ptr[i], |
| clk_rate); |
| if (rc < 0) { |
| pr_err("%s set failed\n", |
| clk_info[i].clk_name); |
| goto cam_clk_set_err; |
| } |
| |
| } else if (clk_info[i].clk_rate == INIT_RATE) { |
| clk_rate = clk_get_rate(clk_ptr[i]); |
| if (clk_rate == 0) { |
| clk_rate = |
| clk_round_rate(clk_ptr[i], 0); |
| if (clk_rate < 0) { |
| pr_err("%s round rate failed\n", |
| clk_info[i].clk_name); |
| goto cam_clk_set_err; |
| } |
| rc = clk_set_rate(clk_ptr[i], |
| clk_rate); |
| if (rc < 0) { |
| pr_err("%s set rate failed\n", |
| clk_info[i].clk_name); |
| goto cam_clk_set_err; |
| } |
| } |
| } |
| rc = clk_prepare(clk_ptr[i]); |
| if (rc < 0) { |
| pr_err("%s prepare failed\n", |
| clk_info[i].clk_name); |
| goto cam_clk_prepare_err; |
| } |
| |
| rc = clk_enable(clk_ptr[i]); |
| if (rc < 0) { |
| pr_err("%s enable failed\n", |
| clk_info[i].clk_name); |
| goto cam_clk_enable_err; |
| } |
| if (clk_info[i].delay > 20) { |
| msleep(clk_info[i].delay); |
| } else if (clk_info[i].delay) { |
| usleep_range(clk_info[i].delay * 1000, |
| (clk_info[i].delay * 1000) + 1000); |
| } |
| } |
| } else { |
| for (i = num_clk - 1; i >= 0; i--) { |
| if (clk_ptr[i] != NULL) { |
| CDBG("%s disable %s\n", __func__, |
| clk_info[i].clk_name); |
| clk_disable(clk_ptr[i]); |
| clk_unprepare(clk_ptr[i]); |
| clk_put(clk_ptr[i]); |
| } |
| } |
| } |
| return rc; |
| |
| |
| cam_clk_enable_err: |
| clk_unprepare(clk_ptr[i]); |
| cam_clk_prepare_err: |
| cam_clk_set_err: |
| clk_put(clk_ptr[i]); |
| cam_clk_get_err: |
| for (i--; i >= 0; i--) { |
| if (clk_ptr[i] != NULL) { |
| clk_disable(clk_ptr[i]); |
| clk_unprepare(clk_ptr[i]); |
| clk_put(clk_ptr[i]); |
| } |
| } |
| return rc; |
| } |
| |
| int msm_camera_config_vreg(struct device *dev, struct camera_vreg_t *cam_vreg, |
| int num_vreg, enum msm_camera_vreg_name_t *vreg_seq, |
| int num_vreg_seq, struct regulator **reg_ptr, int config) |
| { |
| int i = 0, j = 0; |
| int rc = 0; |
| struct camera_vreg_t *curr_vreg; |
| |
| if (num_vreg_seq > num_vreg) { |
| pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__); |
| return -EINVAL; |
| } |
| |
| if (cam_vreg == NULL) { |
| pr_err("%s:%d cam_vreg sequence invalid\n", __func__, __LINE__); |
| return -EINVAL; |
| } |
| |
| if (!num_vreg_seq) |
| num_vreg_seq = num_vreg; |
| |
| if (config) { |
| for (i = 0; i < num_vreg_seq; i++) { |
| if (vreg_seq) { |
| j = vreg_seq[i]; |
| if (j >= num_vreg) |
| continue; |
| } else |
| j = i; |
| curr_vreg = &cam_vreg[j]; |
| reg_ptr[j] = regulator_get(dev, |
| curr_vreg->reg_name); |
| if (IS_ERR_OR_NULL(reg_ptr[j])) { |
| pr_err("%s: %s get failed\n", |
| __func__, |
| curr_vreg->reg_name); |
| reg_ptr[j] = NULL; |
| goto vreg_get_fail; |
| } |
| if (regulator_count_voltages(reg_ptr[j]) > 0) { |
| rc = regulator_set_voltage( |
| reg_ptr[j], |
| curr_vreg->min_voltage, |
| curr_vreg->max_voltage); |
| if (rc < 0) { |
| pr_err("%s: %s set voltage failed\n", |
| __func__, |
| curr_vreg->reg_name); |
| goto vreg_set_voltage_fail; |
| } |
| if (curr_vreg->op_mode >= 0) { |
| rc = regulator_set_load( |
| reg_ptr[j], |
| curr_vreg->op_mode); |
| rc = 0; |
| if (rc < 0) { |
| pr_err( |
| "%s:%s set optimum mode fail\n", |
| __func__, |
| curr_vreg->reg_name); |
| goto vreg_set_opt_mode_fail; |
| } |
| } |
| } |
| } |
| } else { |
| for (i = num_vreg_seq-1; i >= 0; i--) { |
| if (vreg_seq) { |
| j = vreg_seq[i]; |
| if (j >= num_vreg) |
| continue; |
| } else |
| j = i; |
| curr_vreg = &cam_vreg[j]; |
| if (reg_ptr[j]) { |
| if (regulator_count_voltages(reg_ptr[j]) > 0) { |
| if (curr_vreg->op_mode >= 0) { |
| regulator_set_load( |
| reg_ptr[j], 0); |
| } |
| regulator_set_voltage( |
| reg_ptr[j], 0, curr_vreg-> |
| max_voltage); |
| } |
| regulator_put(reg_ptr[j]); |
| reg_ptr[j] = NULL; |
| } |
| } |
| } |
| return 0; |
| |
| vreg_unconfig: |
| if (regulator_count_voltages(reg_ptr[j]) > 0) |
| regulator_set_load(reg_ptr[j], 0); |
| |
| vreg_set_opt_mode_fail: |
| if (regulator_count_voltages(reg_ptr[j]) > 0) |
| regulator_set_voltage(reg_ptr[j], 0, |
| curr_vreg->max_voltage); |
| |
| vreg_set_voltage_fail: |
| regulator_put(reg_ptr[j]); |
| reg_ptr[j] = NULL; |
| |
| vreg_get_fail: |
| for (i--; i >= 0; i--) { |
| if (vreg_seq) { |
| j = vreg_seq[i]; |
| if (j >= num_vreg) |
| continue; |
| } else |
| j = i; |
| curr_vreg = &cam_vreg[j]; |
| goto vreg_unconfig; |
| } |
| return -ENODEV; |
| } |
| |
| int msm_camera_enable_vreg(struct device *dev, struct camera_vreg_t *cam_vreg, |
| int num_vreg, enum msm_camera_vreg_name_t *vreg_seq, |
| int num_vreg_seq, struct regulator **reg_ptr, int enable) |
| { |
| int i = 0, j = 0, rc = 0; |
| |
| if (num_vreg_seq > num_vreg) { |
| pr_err("%s:%d vreg sequence invalid\n", __func__, __LINE__); |
| return -EINVAL; |
| } |
| if (!num_vreg_seq) |
| num_vreg_seq = num_vreg; |
| |
| if (enable) { |
| for (i = 0; i < num_vreg_seq; i++) { |
| if (vreg_seq) { |
| j = vreg_seq[i]; |
| if (j >= num_vreg) |
| continue; |
| } else |
| j = i; |
| if (IS_ERR_OR_NULL(reg_ptr[j])) { |
| pr_err("%s: %s null regulator\n", |
| __func__, cam_vreg[j].reg_name); |
| goto disable_vreg; |
| } |
| rc = regulator_enable(reg_ptr[j]); |
| if (rc < 0) { |
| pr_err("%s: %s enable failed\n", |
| __func__, cam_vreg[j].reg_name); |
| goto disable_vreg; |
| } |
| if (cam_vreg[j].delay > 20) |
| msleep(cam_vreg[j].delay); |
| else if (cam_vreg[j].delay) |
| usleep_range(cam_vreg[j].delay * 1000, |
| (cam_vreg[j].delay * 1000) + 1000); |
| } |
| } else { |
| for (i = num_vreg_seq-1; i >= 0; i--) { |
| if (vreg_seq) { |
| j = vreg_seq[i]; |
| if (j >= num_vreg) |
| continue; |
| } else |
| j = i; |
| if (reg_ptr[j]) { |
| regulator_disable(reg_ptr[j]); |
| if (cam_vreg[j].delay > 20) |
| msleep(cam_vreg[j].delay); |
| else if (cam_vreg[j].delay) |
| usleep_range( |
| cam_vreg[j].delay * 1000, |
| (cam_vreg[j].delay * 1000) |
| + 1000); |
| } |
| } |
| } |
| return rc; |
| disable_vreg: |
| for (i--; i >= 0; i--) { |
| if (vreg_seq) { |
| j = vreg_seq[i]; |
| if (j >= num_vreg) |
| continue; |
| } else |
| j = i; |
| regulator_disable(reg_ptr[j]); |
| if (cam_vreg[j].delay > 20) |
| msleep(cam_vreg[j].delay); |
| else if (cam_vreg[j].delay) |
| usleep_range(cam_vreg[j].delay * 1000, |
| (cam_vreg[j].delay * 1000) + 1000); |
| } |
| return rc; |
| } |
| |
| void msm_camera_bus_scale_cfg(uint32_t bus_perf_client, |
| enum msm_bus_perf_setting perf_setting) |
| { |
| int rc = 0; |
| |
| if (!bus_perf_client) { |
| pr_err("%s: Bus Client NOT Registered!!!\n", __func__); |
| return; |
| } |
| switch (perf_setting) { |
| case S_EXIT: |
| rc = msm_bus_scale_client_update_request(bus_perf_client, 1); |
| msm_bus_scale_unregister_client(bus_perf_client); |
| break; |
| case S_PREVIEW: |
| rc = msm_bus_scale_client_update_request(bus_perf_client, 1); |
| break; |
| case S_VIDEO: |
| rc = msm_bus_scale_client_update_request(bus_perf_client, 2); |
| break; |
| case S_CAPTURE: |
| rc = msm_bus_scale_client_update_request(bus_perf_client, 3); |
| break; |
| case S_ZSL: |
| rc = msm_bus_scale_client_update_request(bus_perf_client, 4); |
| break; |
| case S_LIVESHOT: |
| rc = msm_bus_scale_client_update_request(bus_perf_client, 5); |
| break; |
| case S_DEFAULT: |
| break; |
| default: |
| pr_err("%s: INVALID CASE\n", __func__); |
| } |
| } |
| |
| int msm_camera_set_gpio_table(struct msm_gpio_set_tbl *gpio_tbl, |
| uint8_t gpio_tbl_size, int gpio_en) |
| { |
| int rc = 0, i; |
| |
| if (gpio_en) { |
| for (i = 0; i < gpio_tbl_size; i++) { |
| gpio_set_value_cansleep(gpio_tbl[i].gpio, |
| gpio_tbl[i].flags); |
| usleep_range(gpio_tbl[i].delay, |
| gpio_tbl[i].delay + 1000); |
| } |
| } else { |
| for (i = gpio_tbl_size - 1; i >= 0; i--) { |
| if (gpio_tbl[i].flags) |
| gpio_set_value_cansleep(gpio_tbl[i].gpio, |
| GPIOF_OUT_INIT_LOW); |
| } |
| } |
| return rc; |
| } |
| |
| int msm_camera_config_single_vreg(struct device *dev, |
| struct camera_vreg_t *cam_vreg, struct regulator **reg_ptr, int config) |
| { |
| int rc = 0; |
| const char *vreg_name = NULL; |
| |
| if (!dev || !cam_vreg || !reg_ptr) { |
| pr_err("%s: get failed NULL parameter\n", __func__); |
| goto vreg_get_fail; |
| } |
| if (cam_vreg->type == VREG_TYPE_CUSTOM) { |
| if (cam_vreg->custom_vreg_name == NULL) { |
| pr_err("%s : can't find sub reg name", |
| __func__); |
| goto vreg_get_fail; |
| } |
| vreg_name = cam_vreg->custom_vreg_name; |
| } else { |
| if (cam_vreg->reg_name == NULL) { |
| pr_err("%s : can't find reg name", __func__); |
| goto vreg_get_fail; |
| } |
| vreg_name = cam_vreg->reg_name; |
| } |
| |
| if (config) { |
| CDBG("%s enable %s\n", __func__, vreg_name); |
| *reg_ptr = regulator_get(dev, vreg_name); |
| if (IS_ERR(*reg_ptr)) { |
| pr_err("%s: %s get failed\n", __func__, vreg_name); |
| *reg_ptr = NULL; |
| goto vreg_get_fail; |
| } |
| if (regulator_count_voltages(*reg_ptr) > 0) { |
| CDBG("%s: voltage min=%d, max=%d\n", |
| __func__, cam_vreg->min_voltage, |
| cam_vreg->max_voltage); |
| rc = regulator_set_voltage( |
| *reg_ptr, cam_vreg->min_voltage, |
| cam_vreg->max_voltage); |
| if (rc < 0) { |
| pr_err("%s: %s set voltage failed\n", |
| __func__, vreg_name); |
| goto vreg_set_voltage_fail; |
| } |
| if (cam_vreg->op_mode >= 0) { |
| rc = regulator_set_load(*reg_ptr, |
| cam_vreg->op_mode); |
| if (rc < 0) { |
| pr_err( |
| "%s: %s set optimum mode failed\n", |
| __func__, vreg_name); |
| goto vreg_set_opt_mode_fail; |
| } |
| } |
| } |
| rc = regulator_enable(*reg_ptr); |
| if (rc < 0) { |
| pr_err("%s: %s regulator_enable failed\n", __func__, |
| vreg_name); |
| goto vreg_unconfig; |
| } |
| } else { |
| CDBG("%s disable %s\n", __func__, vreg_name); |
| if (*reg_ptr) { |
| CDBG("%s disable %s\n", __func__, vreg_name); |
| regulator_disable(*reg_ptr); |
| if (regulator_count_voltages(*reg_ptr) > 0) { |
| if (cam_vreg->op_mode >= 0) |
| regulator_set_load(*reg_ptr, 0); |
| regulator_set_voltage( |
| *reg_ptr, 0, cam_vreg->max_voltage); |
| } |
| regulator_put(*reg_ptr); |
| *reg_ptr = NULL; |
| } else { |
| pr_err("%s can't disable %s\n", __func__, vreg_name); |
| } |
| } |
| return 0; |
| |
| vreg_unconfig: |
| if (regulator_count_voltages(*reg_ptr) > 0) |
| regulator_set_load(*reg_ptr, 0); |
| |
| vreg_set_opt_mode_fail: |
| if (regulator_count_voltages(*reg_ptr) > 0) |
| regulator_set_voltage(*reg_ptr, 0, cam_vreg->max_voltage); |
| |
| vreg_set_voltage_fail: |
| regulator_put(*reg_ptr); |
| *reg_ptr = NULL; |
| |
| vreg_get_fail: |
| return -ENODEV; |
| } |
| |
| int msm_camera_request_gpio_table(struct gpio *gpio_tbl, uint8_t size, |
| int gpio_en) |
| { |
| int rc = 0, i = 0, err = 0; |
| |
| if (!gpio_tbl || !size) { |
| pr_err("%s:%d invalid gpio_tbl %pK / size %d\n", __func__, |
| __LINE__, gpio_tbl, size); |
| return -EINVAL; |
| } |
| for (i = 0; i < size; i++) { |
| CDBG("%s:%d i %d, gpio %d dir %ld\n", __func__, __LINE__, i, |
| gpio_tbl[i].gpio, gpio_tbl[i].flags); |
| } |
| if (gpio_en) { |
| for (i = 0; i < size; i++) { |
| err = gpio_request_one(gpio_tbl[i].gpio, |
| gpio_tbl[i].flags, gpio_tbl[i].label); |
| if (err) { |
| /* |
| * After GPIO request fails, contine to |
| * apply new gpios, outout a error message |
| * for driver bringup debug |
| */ |
| pr_err("%s:%d gpio %d:%s request fails\n", |
| __func__, __LINE__, |
| gpio_tbl[i].gpio, gpio_tbl[i].label); |
| } |
| } |
| } else { |
| gpio_free_array(gpio_tbl, size); |
| } |
| return rc; |
| } |
| |
| /* |
| * msm_camera_get_dt_reg_settings - Get dt reg settings from device-tree. |
| * @of_node: Pointer to device of_node from dev. |
| * @dt_prop_name: String of the property to search in of_node from dev. |
| * @reg_s: Double pointer will be allocated by this function and filled. |
| * @size: Pointer to fill the length of the available entries. |
| */ |
| int msm_camera_get_dt_reg_settings(struct device_node *of_node, |
| const char *dt_prop_name, uint32_t **reg_s, |
| unsigned int *size) |
| { |
| int ret; |
| unsigned int cnt; |
| |
| if (!of_node || !dt_prop_name || !size || !reg_s) { |
| pr_err("%s: Error invalid args %pK:%pK:%pK:%pK\n", |
| __func__, size, reg_s, of_node, dt_prop_name); |
| return -EINVAL; |
| } |
| if (!of_get_property(of_node, dt_prop_name, &cnt)) { |
| pr_debug("Missing dt reg settings for %s\n", dt_prop_name); |
| return -ENOENT; |
| } |
| |
| if (!cnt || (cnt % 8)) { |
| pr_err("%s: Error invalid number of entries cnt=%d\n", |
| __func__, cnt); |
| return -EINVAL; |
| } |
| cnt /= 4; |
| if (cnt != 0) { |
| *reg_s = kcalloc(cnt, sizeof(uint32_t), |
| GFP_KERNEL); |
| if (!*reg_s) |
| return -ENOMEM; |
| ret = of_property_read_u32_array(of_node, |
| dt_prop_name, |
| *reg_s, |
| cnt); |
| if (ret < 0) { |
| pr_err("%s: No dt reg info read for %s ret=%d\n", |
| __func__, dt_prop_name, ret); |
| kfree(*reg_s); |
| return -ENOENT; |
| } |
| *size = cnt; |
| } else { |
| pr_err("%s: Error invalid entries\n", __func__); |
| return -EINVAL; |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * msm_camera_get_dt_reg_settings - Free dt reg settings memory. |
| * @reg_s: Double pointer will be allocated by this function and filled. |
| * @size: Pointer to set the length as invalid. |
| */ |
| void msm_camera_put_dt_reg_settings(uint32_t **reg_s, |
| unsigned int *size) |
| { |
| kfree(*reg_s); |
| *reg_s = NULL; |
| *size = 0; |
| } |
| |
| int msm_camera_hw_write_dt_reg_settings(void __iomem *base, |
| uint32_t *reg_s, |
| unsigned int size) |
| { |
| int32_t rc = 0; |
| |
| if (!reg_s || !base || !size) { |
| pr_err("%s: Error invalid args\n", __func__); |
| return -EINVAL; |
| } |
| rc = msm_camera_io_w_reg_block((const u32 *) reg_s, |
| base, size); |
| if (rc < 0) |
| pr_err("%s: Failed dt reg setting write\n", __func__); |
| return rc; |
| } |
| |