【TINY4412】LINUX移植笔记:(20)设备树LCD背光驱动

【TINY4412】LINUX移植笔记:(20)设备树 LCD背光驱动

宿主机 : 虚拟机 Ubuntu 16.04 LTS / X64
目标板[底板]: Tiny4412SDK - 1506
目标板[核心板]: Tiny4412 - 1412
LINUX内核: 4.12.0
交叉编译器: arm-none-linux-gnueabi-gcc(gcc version 4.8.3 20140320)
日期: 2017-8-24 20:04:33
作者: SY

简介

开发板的 LCD 背光通过 CPLD(EPM240T100) 控制,不能直接通过操作 GPIO 电平,控制背光亮度。根据友善之臂的说法,支持 128 级背光调节
这里写图片描述
这里写图片描述

参考友善之臂的驱动 linux-3.5\drivers\input\touchscreen\tiny4412_1wire_host.c

#define GPIO_1WIRE      EXYNOS4_GPX1(2)

可以推出背光相关的引脚:GPX1_2 ,这个引脚在使用时,作为一个双向 IO ,既需要输入,也需要输出。

而且触摸屏的输入也使用该引脚,暂时我们先实现 backlight 功能。

设备树

开发板和 CPLD 通讯使用了特殊的协议,设置一次背光值,需要读写 IO 多次,因此,需要使用定时器功能,我们使用定时器 3 ,也就是 PWM3

参考:PWM 设备树

pwm: pwm@139D0000 {
    compatible = "samsung,exynos4210-pwm";
    reg = <0x139D0000 0x1000>;
    interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>,
    <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>,
    <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
    <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
    <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clock CLK_PWM>;
    clock-names = "timers";
    #pwm-cells = <3>;
    status = "disabled";
};

实现我们的 dts

backlight {
    compatible = "tiny4412, onewire_backlight";
    reg = <0x139D0000 0x1000>;
    pinctrl-names = "backlight_in", "backlight_out";
    pinctrl-0 = <&backlight_in>;
    pinctrl-1 = <&backlight_out>;
    gpios = <&gpx1 2 GPIO_ACTIVE_HIGH>;
    clock-names = "timers";
    clocks = <&clock CLK_PWM>;
    interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
    pwms = <&pwm 0 1000000000 0>;
};

&pinctrl_1 {
    backlight_out: backlight_out {
      samsung,pins = "gpx1-2";
      samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>;
      samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
      samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
    };

    backlight_in: backlight_in {
      samsung,pins = "gpx1-2";
      samsung,pin-function = <EXYNOS_PIN_FUNC_INPUT>;
      samsung,pin-pud = <EXYNOS_PIN_PULL_NONE>;
      samsung,pin-drv = <EXYNOS4_PIN_DRV_LV1>;
    };
};

使用 pinctrl-0 pinctrl-1 可以将同一个引脚设置为 2 种配置。

Makefile

root@ubuntu:/opt/linux-4.12# cat drivers/input/touchscreen/Makefile 

obj-$(CONFIG_TOUCHSCREEN_1WIRE)         += tiny4412_1wire.o

Kconfig

root@ubuntu:/opt/linux-4.12# cat drivers/input/touchscreen/Kconfig 

config TOUCHSCREEN_1WIRE
        tristate "Tiny4412 1wire touchscreen"
        help
          Say Y here to support touchscreen for tiny4412.

          If unsure, say N.

Memenuconfig

Device Drivers  ---> 
    Input device support  --->  
        [*]   Touchscreens  --->
            <*>   Tiny4412 1wire touchscreen

源文件

新建一个tiny4412_1wire.c源文件,参考:学习设备树之(十一)Backlight

/*
 * lcd_key driver for tiny4412
 *
 * Copyright (c) 2017
 * Author: SY <1530454315@qq.com>
 *
 * 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 2 of
 * the License, or (at your option) any later version. 
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h> 
#include <signal.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>

#define KEY_HOME                102
#define KEY_MENU                139
#define KEY_BACK                158

struct input_event {
        struct timeval time;
        unsigned short int type;
        unsigned short int code;
        signed int value;
};

#if 0
static void help(void)
{
        printf("Usage: ./key <id>\n");
}
#endif

bool esc = false;

static void sigint_handler(int dunno)
{
        switch (dunno) {
        case SIGINT:
                esc = true;
                printf("< Ctrl+C > Press.\n");
                break;
        default:
                break;
        }
}

static void* read_handler(void* data)
{
        printf("thread run.\n");

        int epfd = epoll_create1(0);
        if (epfd < 0) {
                perror("epoll_create1");
                return NULL;
        }

        int evfd = open("/dev/input/event0", O_RDONLY);
        if (evfd < 0) {
                perror("[open]");
                esc = true;
        }

        struct epoll_event epoll_event;
        epoll_event.events = EPOLLIN;
        epoll_event.data.fd = evfd;
        if (epoll_ctl(epfd, EPOLL_CTL_ADD, evfd, &epoll_event) < 0) {
                perror("[epoll_ctl]");
                esc = true;
        }

        printf("start epoll...\n");

        struct input_event event;
        const int MAX_EVENT_NUMS = 10;
        const int TIMEOUT = 100;
        struct epoll_event *events = calloc(MAX_EVENT_NUMS, sizeof(struct epoll_event));
        if (!events) {
                perror("mem calloc");
                esc = true;
        }

        while (esc == false) {
                int nums = epoll_wait(epfd, events, MAX_EVENT_NUMS, TIMEOUT);
                for (int i=0; i<nums; ++i) {
                        if (events[i].events & (EPOLLERR | EPOLLHUP)) {
                                perror("epoll");
                                continue;
                        } else if ((events[i].data.fd == evfd) && (events[i].events & EPOLLIN)) {
                                int ret = read(evfd, &event, sizeof(event));
                                if (ret < 0) {
                                        break;
                                }
                                //printf("[key] nums=%d code=%d value=%d\n", nums, event.code, event.value);

                                switch (event.code) {
                case KEY_HOME:
                        if (event.value) {
                                printf("[HOME] Press.\n");
                        } else {
                                printf("[HOME] Release.\n");
                        }
                        break;
                case KEY_MENU:
                        if (event.value) {
                        printf("[MENU] Press.\n");
                        } else {
                        printf("[MENU] Release.\n");
                        }
                        break;
                case KEY_BACK:
                        if (event.value) {
                                printf("[BACK] Press.\n");
                        } else {
                                printf("[BACK] Release.\n");
                                }
                        break;
                default:
                        break;
                }
                        }
                }
        }

        if (events) {
                free(events);
        }
        close(epfd);
        close(evfd);
        printf("thread exit.\n");

        pthread_exit(NULL);

        return NULL;
}

int main(int argc, char **argv)
{
        pthread_t thread_read;
        int ret = pthread_create(&thread_read, NULL, read_handler, NULL);
        if (ret) {
                perror("[thread_create]");
                return 1;
        }

        /* Register signal */
        signal(SIGINT, sigint_handler);

        pthread_join(thread_read, NULL);
        printf("done!\n");

        return 0;
}

在申请 IO 资源时,由于我们的驱动中在 pwm-samsung.cpwm_samsung_probe 中已经申请过,而且不会释放资源,因此:

 /* Get res */
priv->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!priv->res) {
  dev_err(dev, "get res error!\n");
  return -EINVAL;
}
dev_err(dev, "start=%x, end=%x name=%s\n", priv->res->start, priv->res->end, priv->res->name);

/* Get ioremap */
priv->pwm = (volatile struct pwm_base *)devm_ioremap_resource(dev, priv->res);
if (IS_ERR((struct pwm_base *)priv->pwm)) {
  dev_err(dev, "devm_ioremap_resource error!\n");
  return -EINVAL;
}

如果再次申请就会失败。

我们采用另一种方式获取资源,devm_pwm_get

struct pwm_device *pwm;

pwm = devm_pwm_get(dev, NULL);

查看结构体:

struct pwm_device {
    const char *label;
    unsigned long flags;
    unsigned int hwpwm;
    unsigned int pwm;
    struct pwm_chip *chip;
    void *chip_data;

    struct pwm_args args;
    struct pwm_state state;
};

struct pwm_chip {
    struct device *dev;
    struct list_head list;
    const struct pwm_ops *ops;
    int base;
    unsigned int npwm;

    struct pwm_device *pwms;

    struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
                    const struct of_phandle_args *args);
    unsigned int of_pwm_n_cells;
};

关键的字段是 base ,搜索该字段的内容:

static int pwm_samsung_probe(struct platform_device *pdev)
{
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    chip->base = devm_ioremap_resource(&pdev->dev, res);
}

可以看出,在这个驱动中,已经获取过 IO 资源,并把 指针 存储到 base 中,这样就好办了

chip = (struct samsung_pwm_chip *)pwm->chip;
priv->pwm = (volatile struct pwm_base *)chip->base;
if (IS_ERR((struct pwm_base *)priv->pwm)) {
  return -EINVAL;
}

这样,定时器的寄存器基地址被存储到 priv->pwm 中,后面我们就可以自由读写定时器寄存器了。

应用程序

/*
 * Copyright (c) 2017
 * Author: SY <1530454315@qq.com>
 *
 * 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 2 of
 * the License, or (at your option) any later version. 
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h> 
#include <signal.h>
#include <stdbool.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/epoll.h>


#if 0
static void help(void)
{
        printf("Usage: ./key <id>\n");
}
#endif

int main(int argc, char **argv)
{
        int evfd = open("/dev/backlight", O_RDWR);
    if (evfd < 0) {
        perror("[open]");
    }

        for (int i=0; i<=127; i++) {
        uint8_t buff = i;
        if (write(evfd, &buff, 1) < 0) {
            perror("write");
        }
                printf("backlight: %d\n", i);

                usleep(50000);
        }

        close(evfd);
        printf("done!\n");

        return 0;
}

应用程序 Makefile

# Linux modules compile
# Author : SY
# Time   : 2017-8-10 22:29:24
#############################################################################

CC            = arm-none-linux-gnueabi-gcc
CFLAGS        = -Wall -std=gnu99 -pthread


TARGET        = backlight
OBJS          = $(TARGET).o
INSTALL_PATH  = /opt/fs/rootfs/rootfs/tmp/

$(TARGET) : $(OBJS)
        $(CC) $(CFLAGS) -o $@ $<

install:
        chmod 755 $(TARGET)
        cp $(TARGET) $(INSTALL_PATH) 

clean:
        rm -rf *.o $(TARGET) 

测试

查看内核消息

[    2.653749] tiny4412_1wire 139d0000.backlight: probe.
[    2.658219] tiny4412_1wire 139d0000.backlight: clk = 100000000 Hz
[    2.664248] of_get_named_gpiod_flags: parsed 'gpios' property of node '/backlight[0]' - status (0)
[    2.673202] tiny4412_1wire 139d0000.backlight: TCFG0: 00000f01
[    2.678969] tiny4412_1wire 139d0000.backlight: TCFG1: 00020000

找到应用程序

[root@TINY4412:~]# cd tmp/
[root@TINY4412:/tmp]# ./backlight 
[  156.325821] backlight_open
backlight: 0
backlight: 1
backlight: 2
backlight: 3
backlight: 4
backlight: 5
backlight: 6
backlight: 7
backlight: 8
backlight: 9
backlight: 10
backlight: 11
backlight: 12
backlight: 13
backlight: 14
backlight: 15
backlight: 16
backlight: 17
backlight: 18
backlight: 19
backlight: 20
backlight: 21
backlight: 22
backlight: 23
backlight: 24
backlight: 25
backlight: 26
backlight: 27
backlight: 28
backlight: 29
backlight: 30
backlight: 31
backlight: 32
backlight: 33
backlight: 34
backlight: 35
backlight: 36
backlight: 37
backlight: 38
backlight: 39
backlight: 40
backlight: 41
backlight: 42
backlight: 43
backlight: 44
backlight: 45
backlight: 46
backlight: 47
backlight: 48
backlight: 49
backlight: 50
backlight: 51
backlight: 52
backlight: 53
backlight: 54
backlight: 55
backlight: 56
backlight: 57
backlight: 58
backlight: 59
backlight: 60
backlight: 61
backlight: 62
backlight: 63
backlight: 64
backlight: 65
backlight: 66
backlight: 67
backlight: 68
backlight: 69
backlight: 70
backlight: 71
backlight: 72
backlight: 73
backlight: 74
backlight: 75
backlight: 76
backlight: 77
backlight: 78
backlight: 79
backlight: 80
backlight: 81
backlight: 82
backlight: 83
backlight: 84
backlight: 85
backlight: 86
backlight: 87
backlight: 88
backlight: 89
backlight: 90
backlight: 91
backlight: 92
backlight: 93
backlight: 94
backlight: 95
backlight: 96
backlight: 97
backlight: 98
backlight: 99
backlight: 100
backlight: 101
backlight: 102
backlight: 103
backlight: 104
backlight: 105
backlight: 106
backlight: 107
backlight: 108
backlight: 109
backlight: 110
backlight: 111
backlight: 112
backlight: 113
backlight: 114
backlight: 115
backlight: 116
backlight: 117
backlight: 118
backlight: 119
backlight: 120
backlight: 121
backlight: 122
backlight: 123
backlight: 124
backlight: 125
backlight: 126
backlight: 127
[  162.758208] backlight_exit
done!
[root@TINY4412:/tmp]# 

可以看到 LCD 从暗慢慢变亮。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值