展讯T606平台闪光灯驱动调试分析

SPRD

t606_t

原理

闪光灯在展讯t606主要有三种控制方式

  1. 内置PMIC(也称为假闪)

  2. 外置闪光灯IC(也称为真闪)
    1)GPIO控制(aw3641)
    2)GPIO+I2C
    3)PWM控制

  3. 屏幕LCD做补光

代码目录

├── bsp
│   ├── device
│   │   └── qogirl6
│   │       └── androidt
│   │           └── common
│   │               └── modules.cfg //ko模块配置
│   ├── kernel5.4
│   │   └── kernel5.4
│   │       └── arch
│   │           └── arm64
│   │               └── boot
│   │                   └── dts
│   │                       └── sprd //展讯设备树
│   │                           ├── sc2730.dtsi
│   │                           └── ums9230-1h10-overlay.dts
│   └── modules
│       └── common
│           └── camera
│               └── flash
│                   ├── aw3641 //GPIO类型IC驱动
│                   │   ├── flash_ic_aw3641_drv.c
│                   │   ├── Kbuild
│                   │   └── Makefile
│                   ├── flash_drv //通用接口驱动
│                   │   ├── flash_drv.c
│                   │   ├── flash_drv.h
│                   │   ├── Kbuild
│                   │   └── Makefile
│                   └── sc2730_kpled //PMIC类型驱动
│                       ├── Kbuild
│                       ├── Makefile
│                       ├── sc2730_kpled_drv.c
│                       └── sc2730_kpled_reg.h
└── device
    └── sprd
        └── mpool
            └── module
                └── others
                    └── bsp
                        └── mfeature
                            └── kernel
                                └── kernel5.4
                                    └── msoc
                                        └── qogirl6
                                            └── ko
                                                ├── km.mk //ko模块配置
                                                └── modules.load //ko模块顺序

代码分析code

  1. 在接口驱动flash_drv.c中定义了一些接口,包括打开闪光灯open_highlight 和打开手电筒open_torch 等,上层调用驱动时则会调用这些接口

    static const struct sprd_flash_driver_ops flash_gpio_ops = {
    	.open_torch = sprd_flash_aw3641_open_torch,
    	.close_torch = sprd_flash_aw3641_close_torch,
    	.open_preflash = sprd_flash_aw3641_open_preflash,
    	.close_preflash = sprd_flash_aw3641_close_preflash,
    	.open_highlight = sprd_flash_aw3641_open_highlight,
    	.close_highlight = sprd_flash_aw3641_close_highlight,
    	.cfg_value_torch = sprd_flash_aw3641_cfg_value_torch,
    	.cfg_value_preflash = sprd_flash_aw3641_cfg_value_preflash,
    	.cfg_value_highlight = sprd_flash_aw3641_cfg_value_highlight,
    };
    
  2. 而具体的接口实现,则需要加上具体的IC驱动,那么如何让通用接口调用到IC驱动对应的函数实现呢?继续看flash_drv.c
    在这段代码中,定义了一个设备结构flash_device 与对应的结构体s_flash_dev,结构体中的ops[SPRD_FLASH_MAX]则指向IC驱动中的实现函数driver_data[SPRD_FLASH_MAX]则为驱动的各种数据
    函数sprd_flash_register就是用于注册不同IC驱动的函数(指针赋值),使用EXPORT_SYMBOL(sprd_flash_register);导出供其他驱动使用,此函数的形参有三个,分别是实现函数指针ops,驱动数据*drvd,前后摄flash_idx

    enum {
    	SPRD_FLASH_REAR = 0,
    	SPRD_FLASH_FRONT = 1,
    	SPRD_FLASH_MAX,
    };
    
    /* Structure Definitions */
    
    struct flash_device {
    	struct miscdevice md;
    	const struct sprd_flash_driver_ops *ops[SPRD_FLASH_MAX];
    	void *driver_data[SPRD_FLASH_MAX];
    	int flashlight_status[SPRD_FLASH_MAX];
    	unsigned short attr_test_value;
    	char *flash_ic_name;
    };
    
    /* Static Variables Definitions */
    static struct platform_device *pdev;
    static struct flash_device *s_flash_dev;
    
    int sprd_flash_register(const struct sprd_flash_driver_ops *ops,
    			void *drvd,
    			uint8_t flash_idx)
    {
    	if (!s_flash_dev)
    		return -EPROBE_DEFER;
    
    	s_flash_dev->ops[flash_idx] = ops;
    	s_flash_dev->driver_data[flash_idx] = drvd;
    
    	return 0;
    }
    
    EXPORT_SYMBOL(sprd_flash_register);
    
  3. 在IC驱动的probe中可看到使用注册函数sprd_flash_register

    static int sprd_flash_aw3641_probe(struct platform_device *pdev)
    {
    //……
    	ret = sprd_flash_register(&flash_gpio_ops, drv_data, SPRD_FLASH_REAR);
    
    exit:
    	return ret;
    }
    
    static int sprd_flash_SC2730_probe(struct platform_device *pdev)
    {
    //……
    #ifdef FRONT_FLASH
    	ret = sprd_flash_register(&flash_sc2730_front_ops, drv_data,
    							1);
    #endif
    //……
    	ret = sprd_flash_register(&flash_SC2730_ops, drv_data,
    							flash_idx);
    	if (ret < 0)
    		goto exit;
    //……
    }
    
  4. 接下来看实现函数调用,以下用模拟高闪作为例子

    1. 通用驱动

      static int flash_open_highlight(struct flash_device *dev,
      			    uint8_t flash_idx, uint8_t led_idx)
      {
      	int ret = -EPERM;
      
      	if (!dev || !dev->ops[flash_idx])
      		goto exit;
      
      	if (dev->ops[flash_idx]->open_highlight)
      		ret = dev->ops[flash_idx]->open_highlight(
      				dev->driver_data[flash_idx], led_idx);
      exit:
      	return ret;
      }
      
    2. IC驱动

      以下是aw3641驱动,调用关系不深,可以看到在open_highlight中先关闭了FLASH口和EN口,然后调用了模拟PWM的函数open_pwm,通过定时开关实现pwm等级控制

      static int sprd_flash_aw3641_open_highlight(void *drvd, uint8_t idx)
      {
      	int ret = 0;
      	int gpio_id = 0;
      	struct flash_driver_data *drv_data = (struct flash_driver_data *)drvd;
      	if (!drv_data)
      			return -EFAULT;
      
      	pr_info("highlight_opened:%d\n", highlight_opened);
      	if(1 ==  highlight_opened) {
      		gpio_id = drv_data->gpio_tab[GPIO_FLASH_TORCH_MODE];
      		if (gpio_is_valid(gpio_id)) {
      			ret = gpio_direction_output(gpio_id, SPRD_FLASH_OFF);
      			if (ret)
      				goto exit;
      		}
      		gpio_id = drv_data->gpio_tab[GPIO_CHIP_EN];
      		if (gpio_is_valid(gpio_id)) {
      			ret = gpio_direction_output(gpio_id, SPRD_FLASH_OFF);
      			if (ret)
      				goto exit;
      		}
      		udelay(550);
      	}
      	idx = drv_data->torch_led_index;
      	ret = sprd_flash_aw3641_open_pwm(drv_data, idx, g_high_level);
      	if (ret)
      		goto exit;
      	highlight_opened = 1;
      exit:
      	return ret;
      }
      
      static int sprd_flash_aw3641_open_pwm(void *drvd, uint8_t idx, int level)
      {
      	int ret = 0;
      	int i = 0;
      	int gpio_id = 0;
      	unsigned long flags;
      
      	struct flash_driver_data *drv_data = (struct flash_driver_data *)drvd;
      	if (!drv_data)
      		return -EFAULT;
      
      	if (level > FLASH_MAX_LEVEL)
      		level = FLASH_MAX_LEVEL;
      
      	if (level < FLASH_MIN_LEVEL)
      		level = FLASH_MIN_LEVEL;
      
      	idx = drv_data->torch_led_index;
      	if (SPRD_FLASH_LED0 & idx) {
      		gpio_id = drv_data->gpio_tab[GPIO_FLASH_TORCH_MODE];
      		if (gpio_is_valid(gpio_id)) {
      			ret = gpio_direction_output(gpio_id, SPRD_FLASH_ON);
      		}
      	}
      
      	if (SPRD_FLASH_LED0 & idx) {
      		gpio_id = drv_data->gpio_tab[GPIO_CHIP_EN];
      		if (gpio_is_valid(gpio_id)) {
      			ret = gpio_direction_output(gpio_id, SPRD_FLASH_OFF);
      			udelay(550);
      			spin_lock_irqsave(&flash_lock, flags);
      			for (i = 0; i < FLASH_MAX_PWM - level; i++) {
      				pr_info("open_pwm:%d\n", i);
      				ret = gpio_direction_output(gpio_id, SPRD_FLASH_OFF);
      				udelay(2);
      				ret = gpio_direction_output(gpio_id, SPRD_FLASH_ON);
      				udelay(2);
      			}
      			spin_unlock_irqrestore(&flash_lock, flags);
      		}
      	}
      	return ret;
      }
      

      以下是SC2730驱动,顺着调用关系往下查可看到最终为寄存器操作,通过PMIC寄存器修改对应LDO的状态

      static int sprd_flash_SC2730_open_highlight(void *drvd, uint8_t idx)
      {
      	struct flash_driver_data *drv_data = (struct flash_driver_data *)drvd;
      #ifdef GPIO_FLASH
      //……
      #endif
      
      #ifdef KEYPAD_LED_FLASH///for add kpled flash
      	sprd_kpled_enable(drv_data);
      #else
      //……
      #endif
      	return 0;
      }
      
      static void sprd_kpled_enable(struct flash_driver_data *led)
      {
      	if (led->run_mode == 1)  /* current mode */
      		sprd_kpled_current_switch(led, KPLED_SWITCH_ON);
      	else  /* ldo mode */
      		sprd_kpled_ldo_switch(led, KPLED_SWITCH_ON);
      
      	PRINT_INFO("sprd_kpled_enable\n");
      #if defined (JZHK_KPLED_FLASH_60MA)
      	sprd_kpled_set_brightness(led,253);
      #elif defined (JZHK_KPLED_FLASH_40MA)
      	sprd_kpled_set_brightness(led,251);
      #else
      	sprd_kpled_set_brightness(led,255);
      #endif
      }
      
      static void sprd_kpled_set_brightness(struct flash_driver_data *led, unsigned long value)
      {
      	unsigned long brightness = value;
      	unsigned long brightness_level;
      	unsigned int ldo_reg;
      	unsigned int ldo_v_shift;
      	unsigned int ldo_v_mask;
      
      	brightness_level = brightness;
      
      	PRINT_INFO("sprd_kpled_set_brightness:led->run_mode = %d\n",
      		   led->run_mode);
      	if (brightness_level > 255)
      		brightness_level = 255;
      
      	if (brightness_level > led->brightness_max)
      		brightness_level = led->brightness_max;
      
      	if (brightness_level < led->brightness_min)
      		brightness_level = led->brightness_min;
      
      //	brightness_level = brightness_level/16;
      	/*brightness steps = 16 */
      
      	if (led->run_mode == 1) {
      		regmap_update_bits(led->reg_map,
      				   led->reg_kpled_ctrl0,
      				   KPLED_V_MSK,
      				   ((brightness_level << KPLED_V_SHIFT) &
      				    KPLED_V_MSK));
      		PRINT_INFO("reg:0x%08X set_val:0x%08X  brightness:%ld\n",
      			led->reg_kpled_ctrl0,
      			kpled_read(led,
      			 led->reg_kpled_ctrl0),
      			brightness);
      	} else {
      		if (led->chip_version == SC2730_KPLED) {
      			ldo_reg = led->reg_kpled_ctrl1,
      			ldo_v_shift = 7;
      			ldo_v_mask = 0xff << ldo_v_shift;
      		} else {
      			ldo_reg = led->reg_kpled_ctrl0,
      			ldo_v_shift = 0;
      			ldo_v_mask = 0xff << ldo_v_shift;
      		}
      			regmap_update_bits(led->reg_map,
      					ldo_reg,
      					ldo_v_mask,
      					((brightness_level << ldo_v_shift)
      					 & ldo_v_mask));
      			PRINT_INFO("reg:0x%08X set_val:0x%08X brightness:%ld\n",
      					ldo_reg,
      					kpled_read(led,
      						ldo_reg),
      					brightness);
      	}
      }
      

设备树dts

sc2730

&adi_bus {
		sc2730_pmic: pmic@0 {
			compatible = "sprd,sc2730";
			reg = <0>;
			spi-max-frequency = <26000000>;
			interrupt-controller;
			#interrupt-cells = <1>;
			#address-cells = <1>;
			#size-cells = <0>;
			pmic_kpled: kpled@1b88 {
				compatible = "sprd,sc27xx-kpled", "sprd,sc2730-kpled";
				brightness_max = <255>;
				brightness_min = <0>;
				run_mode = <1>; /* default current mode */
				reg = <0x1b88>,<0x1b8c>;
			};
}

aw3641

/ {

		model = "Spreadtrum UMS9230 1H10 Board";
	
		compatible = "sprd,ums9230-1h10";
	
		sprd,sc-id = "ums9230 1h10 1000";
	
		sprd,board_id = <0x1100>; //XX00:base board,XX00~XXFF:customize board
	
		fragment {
			target-path = "/";
			__overlay__ {
				flash-aw3641 {
      				compatible = "sprd,flash-aw3641";
							flash-ic = <0>;
       				flash-torch-en-gpios = <&ap_gpio 8 GPIO_ACTIVE_HIGH>;
       				flash-en-gpios = <&ap_gpio 32 GPIO_ACTIVE_HIGH>;
   			};
}

adb测试节点

sys/class/misc/sprd_flash/test

通过往节点写入0~9来测试闪光灯功能,使用前需要打开sprd_flash驱动flash_drv.c中的宏FLASH_TEST

0:flash_open_preflash

1:flash_close_preflash

2:flash_open_torch

3:flash_close_torch

4:flash_open_highlight

5:flash_close_highlight

6:flash_cfg_value_preflash

7:flash_cfg_value_torch

8:flash_cfg_value_highlight

9:pr_info(“%s %s”, flash_capacity.flash_ic_name, flash_dev->flash_ic_name);

测试节点代码:这段代码主要是通过DEVICE_ATTR和device_create_file来实现目标机节点与驱动的交互

#ifdef FLASH_TEST
static struct sprd_flash_capacity flash_capacity = {0};
static ssize_t flash_sysfs_test(struct device *dev,
				struct device_attribute *attr, const char *buf,
				size_t size)
{
	int ret = 0;
	unsigned int val, cmd, led_idx;
	unsigned int flash_idx = 0;
	struct flash_device *flash_dev = NULL;
	struct sprd_flash_element element;

	flash_dev = dev->platform_data;
	if (!flash_dev) {
		pr_err("flash device is null\n");
		return 0;
	}

	ret = kstrtouint(buf, 16, &val);
	if (ret)
		goto exit;

	flash_dev->attr_test_value = val;

	cmd = val & 0x0f;
	led_idx = (val & 0xf0) >> 4;
	element.index = (val & 0x1f00) >> 8;
	flash_idx = (val & 0x8000) >> 15;

	pr_info("cmd:%d flash:%d element.index %d flash_idx %d\n",
			cmd, led_idx, element.index, flash_idx);

	switch (cmd) {
	case 0:
		flash_open_preflash(flash_dev, flash_idx, led_idx);
		break;
	case 1:
		flash_close_preflash(flash_dev, flash_idx, led_idx);
		break;
	case 2:
		flash_dev->flashlight_status[0] = 1;
		flash_open_torch(flash_dev, flash_idx, led_idx);
		break;
	case 3:
		flash_dev->flashlight_status[0] = 0;
		flash_close_torch(flash_dev, flash_idx, led_idx);
		break;
	case 4:
		flash_open_highlight(flash_dev, flash_idx, led_idx);
		break;
	case 5:
		flash_close_highlight(flash_dev, flash_idx, led_idx);
		break;
	case 6:
		flash_cfg_value_preflash(flash_dev, flash_idx,
					 led_idx, &element);
		break;
	case 7:
		flash_cfg_value_torch(flash_dev, flash_idx, led_idx, &element);
		break;
	case 8:
		flash_cfg_value_highlight(flash_dev, flash_idx,
					 led_idx, &element);
		break;
	case 9:
		flash_capacity = flash_get_flash_info(flash_dev, flash_idx,
							   led_idx, &flash_capacity);
		flash_dev->flash_ic_name = flash_capacity.flash_ic_name;
		pr_info("%s %s", flash_capacity.flash_ic_name, flash_dev->flash_ic_name);
		break;
	default:
		break;
	}

	return size;

exit:
	return ret;
}

static ssize_t show_help(struct device *dev,
			 struct device_attribute *attr, char *buf)
{
	struct flash_device *flash_dev = NULL;

	flash_dev = dev->platform_data;
	if (!flash_dev) {
		pr_err("flash device is null\n");
		return 0;
	}
	if((flash_dev->attr_test_value & 0x0f) == 0x9)
		return sprintf(buf, "%s\n", flash_capacity.flash_ic_name);

	return sprintf(buf, "%x\n", flash_dev->attr_test_value);
}

static DEVICE_ATTR(test, S_IRUSR | S_IWUSR, show_help, flash_sysfs_test);
#endif

static int sprd_flash_probe(struct platform_device *pdev)
{
//……
#ifdef FLASH_TEST
	ret = device_create_file(flash_dev->md.this_device, &dev_attr_test);
	if (ret < 0) {
		pr_err("failed to create flash test file");
		goto fail;
	}
#endif
//……
}
### 展锐平台上的闪光灯与电量管理 #### 1. 电量管理概述 在展锐平台上,电量管理是一个复杂而重要的模块。由于Wi-Fi功耗测试受多种因素影响,如环境条件和接入点的选择,因此具体的功耗数值会有所变化[^1]。然而,在设计电量管理系统时,通常考虑以下几个方面: - **电源模式切换**:支持不同的工作状态(如休眠、待机、激活),以优化不同场景下的能耗。 - **动态电压频率调整 (DVFS)**:通过改变处理器的工作频率来降低不必要的电力消耗。 ```c // C代码示例:设置CPU频率 int set_cpu_freq(int freq_khz); ``` #### 2. 闪光灯控制机制 对于相机系统的开发而言,闪光灯作为辅助光源起着至关重要的作用。从评估定案到最后的产品发布,整个过程中涉及到多个环节和技术细节[^2]。具体来说,闪光灯驱动程序需要精心编写,确保其能够稳定可靠地响应拍照指令并提供足够的光照强度。 - **硬件接口定义**:明确GPIO引脚配置以及电流限制参数。 - **软件API实现**:创建易于调用的功能函数用于开启/关闭闪光灯。 ```java // Java代码片段:打开或关闭LED手电筒功能 public void toggleFlashlight(boolean enable){ CameraManager manager = (CameraManager)getSystemService(Context.CAMERA_SERVICE); try { String cameraId = manager.getCameraIdList()[0]; manager.setTorchMode(cameraId, enable); } catch (Exception e) { Log.e("Error", "Failed to access the flashlight"); } } ``` #### 3. 综合考量与最佳实践 为了更好地管理和利用有限的电池资源,建议采取以下措施: - 定期监测剩余电量水平,并据此调整应用程序的行为逻辑; - 对于长时间运行的任务,优先采用低功率算法; - 结合用户习惯设定合理的省电策略,比如自动熄屏时间等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值