linux驅動開發:背光設備

上一篇的pwm 驅動,加上第一篇的led燈控制,合起來就是背光設備的驅動!
背光設備定位於僅僅對於lcd的背光進行控制,其中主要控制背光開關腳(GPIO),背光調節腳(PWM)!
由於很類似,在這裏就不做贅述

顯而易見,pwm的配置和上一篇一樣,具體代碼如下:

/*
 * linux/drivers/char/smart210_pwm.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/gpio.h>

#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>


#define DEVICE_NAME             "pwm-backlight"


#define PWM_IOCTL_SET_BL_POWER      0
#define PWM_IOCTL_SET_BL_FREQ       1       
#define PWM_IOCTL_SET_BL_DUTY       2

#define NS_IN_1HZ               (1000000000UL)


#define BACKLIGHT_PWM_ID            1
#define BACKLIGHT_PWM_GPIO          S5PV210_GPD0(1)

#define BACKLIGHT_CON_GPIO          S5PV210_GPH1(2)

#define _BL_ON                      1
#define _BL_OFF                     0

struct backlight_data{
    unsigned char bl_power;//backlight power pin state
    unsigned char bl_duty;//backlight con for brightness
    unsigned int  bl_freq;//backlight con for freq
};

static struct pwm_device *pwm1backlight;

static struct semaphore lock;

//default setting for backlight ,backlight power on,bl bri is 90,bl freq is 1000hz
static struct backlight_data smart210_bl ={
    .bl_power = _BL_ON,
    .bl_duty  = 90,
    .bl_freq  = 1000,
};

static void bl_set_power(unsigned char blpower) {
    smart210_bl.bl_power = blpower;
    if (gpio_request(BACKLIGHT_CON_GPIO, "BacklightPower")) {
        printk("request GPIO %d for bl power failed\n", BACKLIGHT_CON_GPIO);
        return;
    }
    gpio_set_value(BACKLIGHT_CON_GPIO, blpower);
    s3c_gpio_cfgpin(BACKLIGHT_CON_GPIO, S3C_GPIO_OUTPUT);
    gpio_free(BACKLIGHT_CON_GPIO);  
}

static void pwm_set_data(unsigned int freq,unsigned char duty) {

    unsigned long period_ns = NS_IN_1HZ / freq;
    unsigned long duty_ns =period_ns/100*(100-duty);
    smart210_bl.bl_freq =freq;
    smart210_bl.bl_duty =duty;

    //(100,100)黑色
    //(0,100)白色
    pwm_config(pwm1backlight, duty_ns, period_ns);
    pwm_enable(pwm1backlight);
    s3c_gpio_cfgpin(BACKLIGHT_PWM_GPIO, S3C_GPIO_SFN(2));
}
static int smart210_pwm_open(struct inode *inode, struct file *file) {
    if (!down_trylock(&lock))
        return 0;
    else
        return -EBUSY;
}

static int smart210_pwm_close(struct inode *inode, struct file *file) {
    up(&lock);
    return 0;
}
/*cmd--duty;arg--freq*/
static long smart210_bl_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    //if(cmd ==100) cmd=99;
    //pwm_set_data(arg,cmd);

    switch (cmd) {
        case PWM_IOCTL_SET_BL_POWER:
            bl_set_power(arg);
            break;

        case PWM_IOCTL_SET_BL_FREQ:
            pwm_set_data(arg,smart210_bl.bl_duty);
            break;
        case PWM_IOCTL_SET_BL_DUTY:
            pwm_set_data(smart210_bl.bl_freq,arg);
            break;
        default:
            break;
    }
    return 0;
}
static ssize_t smart210_bl_write(struct file *file, const char *buffer,
        size_t count, loff_t *ppos)
{
    unsigned char *k_buf =(char *)smart210_bl;
    int ret;

    if (count == 0) {
        return count;
    }

    ret = copy_from_user(k_buf, buffer, sizeof(struct backlight_data)) ? -EFAULT : 0;//to_kernel,from_usr,length
    if (ret) {
        return ret;
    }
    bl_set_power(smart210_bl.bl_power);
    pwm_set_data(smart210_bl.bl_freq,smart210_bl.bl_duty);
    return count;
}
static ssize_t smart210_bl_read(struct file *filp, char *buffer,
        size_t count, loff_t *ppos)
{
    int ret;
    unsigned char *k_buf =(char *)smart210_bl;

    if (count == 0) {
        return 0;
    }

    ret = copy_to_user(buffer, k_buf, sizeof(struct backlight_data)) ? -EFAULT : 0;//to_user,from,length
    if (ret) {
        return ret;
    }

    return sizeof(struct backlight_data);
}
static struct file_operations smart210_pwm_ops = {
    .owner          = THIS_MODULE,
    .open           = smart210_pwm_open,
    .release        = smart210_pwm_close, 
    .unlocked_ioctl = smart210_bl_ioctl,
    .read           = smart210_bl_read,
    .write          = smart210_bl_write,
};

static struct miscdevice smart210_misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &smart210_pwm_ops,


};

static int __init smart210_pwm_dev_init(void) {
    int ret;

    ret = gpio_request(BACKLIGHT_PWM_GPIO, DEVICE_NAME);
    if (ret) {
        printk("request GPIO %d for pwm failed\n", BACKLIGHT_PWM_GPIO);
        return ret;
    }

    gpio_set_value(BACKLIGHT_PWM_GPIO, 0);
    s3c_gpio_cfgpin(BACKLIGHT_PWM_GPIO, S3C_GPIO_OUTPUT);

    pwm1backlight = pwm_request(BACKLIGHT_PWM_ID, DEVICE_NAME);
    if (IS_ERR(pwm1backlight)) {
        printk("request pwm %d for %s failed\n", BACKLIGHT_PWM_ID, DEVICE_NAME);
        return -ENODEV;
    }



    sema_init(&lock, 1);
    ret = misc_register(&smart210_misc_dev);

    printk(DEVICE_NAME "\tinitialized\n");

    return ret;
}

static void __exit smart210_pwm_dev_exit(void) {


    misc_deregister(&smart210_misc_dev);
    gpio_free(BACKLIGHT_PWM_GPIO);
}

module_init(smart210_pwm_dev_init);
module_exit(smart210_pwm_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
MODULE_DESCRIPTION("S5PV210 PWM Backlight Driver");

背光設備,根據實際情況來講,一般都是固定 背光Adj腳的Freq,僅僅改變它的duty來控制亮度。另外背光的開關也可以單獨控制。這裏我們實現3個功能,背光開關,freq控制和duty控制。

嗯,實際上有時我們也需要獲得當前背光的亮度,背光的開光狀態等,所以我們需要讀設備。同樣我們可以對設備進行寫!所以這裏在初始化smart210_pwm_ops 這邊時加入了設備讀寫接口

 ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 //file,user buffer,length,相對文件頭的偏移量,將設備的資訊抓到用戶空間
 ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 //file,user buffer,length,相對文件頭的偏移量,將用戶空間的資訊寫到設備中
內核空間和用戶空間
驅動是運行在內核空間的,測試程序是運行在用戶空間的。所以在從內核空間copy資料到用戶空間,或者把用戶空間的資料copy到內核空間時,便需要專用的函數。

static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
//用戶空間buffer,內核空間buffer,長度

static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
//內核空間buffer,用戶空間buffer,長度

同樣,pwm需要先初始化它的timer
在arch/arm/mach-s5pv210/mach-smdkv210.c中添加

static struct platform_device *smdkv210_devices[] __initdata = {
    &s3c_device_adc,
    &s3c_device_cfcon,
    &s3c_device_fb,
    &s3c_device_hsmmc0,
    &s3c_device_hsmmc1,
    &s3c_device_hsmmc2,
    &s3c_device_hsmmc3,
    &s3c_device_i2c0,
    &s3c_device_i2c1,
    &s3c_device_i2c2,
    &s3c_device_rtc,
    &s3c_device_ts,
    &s3c_device_usb_hsotg,
    &s3c_device_wdt,
    &s3c_device_timer[0],// for pwm buzzer
    &s3c_device_timer[1],// for pwm backlight
    &s5p_device_fimc0,
    &s5p_device_fimc1,
    &s5p_device_fimc2,
    &s5p_device_fimc_md,
    &s5p_device_jpeg,
    &s5p_device_mfc,
    &s5p_device_mfc_l,
    &s5p_device_mfc_r,
    &s5pv210_device_ac97,
    &s5pv210_device_iis0,
    &s5pv210_device_spdif,
    &samsung_asoc_idma,
    &samsung_device_keypad,
    &smdkv210_dm9000,
    &smdkv210_lcd_lte480wv,
};

這裏同樣將驅動關聯進內核,編譯,加載,在根文件系統的dev目錄下會自動生成pwm-backlight設備文件,這裏已經加載成功!

測試文件:

#include <stdio.h>
#include <termios.h>
#include <unistd.h>//close read,write func
#include <stdlib.h>
#include <sys/ioctl.h>
/*open head need*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>



#define PWM_IOCTL_SET_BL_POWER      3
#define PWM_IOCTL_SET_BL_FREQ       4       
#define PWM_IOCTL_SET_BL_DUTY       5

#define CMD_MIN             PWM_IOCTL_SET_BL_POWER
#define CMD_MAX             PWM_IOCTL_SET_BL_DUTY

#define _BL_ON              1
#define _BL_OFF             0


struct backlight_data{
    unsigned char bl_power;//backlight power pin state
    unsigned int bl_duty;//backlight con for brightness
    unsigned int  bl_freq;//backlight con for freq
};

struct backlight_data us_bl_data;
static int fd = -1;



/*cmd data*/

int main(int argc, char **argv)
{
    unsigned int cmd,data;
    int ret;
    if (argc != 3) {
        fprintf(stderr, "Usage: pwm_bl_test  cmd data.\n");
        exit(1);
    }
    else
    {
        sscanf(argv[1], "%d", &cmd);
        sscanf(argv[2], "%d", &data);
        printf("cmd:%d,data:%d\n",cmd,data);

        if(cmd >CMD_MAX || cmd <CMD_MIN){
            fprintf(stderr, "Usage: pwm_bl_test cmd range: 3-5\n");
            exit(1);
        }

        //cmd =0,check data range
        switch(cmd)
        {
            case PWM_IOCTL_SET_BL_POWER:
                if(data >1 || data <0){
                fprintf(stderr, "Usage: pwm_bl_test data range: 0 - 1\n");
                exit(1);
                }
                break;
            case PWM_IOCTL_SET_BL_FREQ:
                if(data >2000 || data <10){
                fprintf(stderr, "Usage: pwm_bl_test data range: 10 - 2000\n");
                exit(1);
                }
                break;
            case PWM_IOCTL_SET_BL_DUTY:
                if(data >99 || data <0){
                fprintf(stderr, "Usage: pwm_bl_test data range: 0 - 99\n");
                exit(1);
                }
                break;
            default:
            break;
        }
        fd = open("/dev/pwm-backlight", 0);
        if (fd < 0) {
        perror("open pwm_backlight device");
        exit(1);
        }
        ret = ioctl(fd,cmd,data);
        //從設備讀狀態
        read(fd, (char*)(&us_bl_data), sizeof(struct backlight_data));

        printf("freq=%d,duty=%d,power=%d\n",us_bl_data.bl_freq,us_bl_data.bl_duty,us_bl_data.bl_power);
        if(ret < 0) {
        perror("set the backlight");
        exit(1);
        }
        close(fd);
    }





    return 0;
}

經過測試,可以正常控制背光開關,freq,duty。並可以從設備讀取到已經設定的背光設備參數,至此,背光設備的驅動基本完成!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章