36.【linux驱动】spi framebuffer驱动(切换并显示虚拟终端)

1.framebuffer驱动
2.spi framebuffer驱动
3.spi framebuffer驱动(切换并显示虚拟终端)

切换终端输出

接这上一节
spi framebuffer驱动实现了,但是只有刷屏。6个虚拟终端并没有显示在屏幕上,要想办法让虚拟终端显示到我们的fb上面来。最总虚拟终端的源代码(过程不详述),发现了这么个地方:
/driver/video/console/fbcon.c

static int fbcon_event_notify(struct notifier_block *self,
			      unsigned long action, void *data)
{
	struct fb_event *event = data;
	struct fb_info *info = event->info;
	struct fb_videomode *mode;
	struct fb_con2fbmap *con2fb;
	struct fb_blit_caps *caps;
	int idx, ret = 0;

	/*
	 * ignore all events except driver registration and deregistration
	 * if fbcon is not active
	 */
	if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
				  action == FB_EVENT_FB_UNREGISTERED))
		goto done;

	switch(action) {
	case FB_EVENT_SUSPEND:
		fbcon_suspended(info);
		break;
	case FB_EVENT_RESUME:
		fbcon_resumed(info);
		break;
	case FB_EVENT_MODE_CHANGE:
		fbcon_modechanged(info);
		break;
	case FB_EVENT_MODE_CHANGE_ALL:
		fbcon_set_all_vcs(info);
		break;
	case FB_EVENT_MODE_DELETE:
		mode = event->data;
		ret = fbcon_mode_deleted(info, mode);
		break;
	case FB_EVENT_FB_UNBIND:
		idx = info->node;
		ret = fbcon_fb_unbind(idx);
		break;
	case FB_EVENT_FB_REGISTERED:
		ret = fbcon_fb_registered(info);
		break;
	case FB_EVENT_FB_UNREGISTERED:
		ret = fbcon_fb_unregistered(info);
		break;
	case FB_EVENT_SET_CONSOLE_MAP:
		con2fb = event->data;
		ret = set_con2fb_map(con2fb->console - 1,
				     con2fb->framebuffer, 1);
		break;
	case FB_EVENT_GET_CONSOLE_MAP:
		con2fb = event->data;
		con2fb->framebuffer = con2fb_map[con2fb->console - 1];
		break;
	case FB_EVENT_BLANK:
		fbcon_fb_blanked(info, *(int *)event->data);
		break;
	case FB_EVENT_NEW_MODELIST:
		fbcon_new_modelist(info);
		break;
	case FB_EVENT_GET_REQ:
		caps = event->data;
		fbcon_get_requirement(info, caps);
		break;
	case FB_EVENT_REMAP_ALL_CONSOLE:
		idx = info->node;
		fbcon_remap_all(idx);
		break;
	}
done:
	return ret;
}

切换虚拟终端输出就在这个地方了,只需要发送一个fb_notify消息即可。我们使用最后这个

	case FB_EVENT_REMAP_ALL_CONSOLE:
		idx = info->node;
		fbcon_remap_all(idx);
		break;

切换所有终端。源码

struct fb_info * fb_init(struct spi_device *spi) //此函数在spi设备驱动的probe函数里被调用
{
    struct fb_info *fbi;
    u8 *v_addr;
    u32 p_addr;
    lcd_data_t *data;
    struct fb_event event;

    v_addr = dma_alloc_coherent(NULL, LCD_W * LCD_H * 3, &p_addr, GFP_KERNEL);

    //额外分配lcd_data_t类型空间
    fbi = framebuffer_alloc(sizeof(lcd_data_t), NULL);
    data = fbi->par; //data指针指向额外分配的空间

    data->spi = spi;

    fbi->var.xres = LCD_W;
    fbi->var.yres = LCD_H;
    fbi->var.xres_virtual = LCD_W;
    fbi->var.yres_virtual = LCD_H;
    fbi->var.bits_per_pixel = 24;
    fbi->var.red.offset = 16;
    fbi->var.red.length = 5;
    fbi->var.green.offset = 8;
    fbi->var.green.length = 6;
    fbi->var.blue.offset = 0;
    fbi->var.blue.length = 5;
    // fbi->var.pixclock   = 93006;
    fbi->var.activate    = FB_ACTIVATE_NOW;

    strcpy(fbi->fix.id, "hello_fb");
    fbi->fix.smem_start = p_addr; //显存的物理地址
    fbi->fix.smem_len = LCD_W * LCD_H * 3;
    fbi->fix.type = FB_TYPE_PACKED_PIXELS;
    fbi->fix.visual = FB_VISUAL_TRUECOLOR;
    fbi->fix.line_length = LCD_W * 3;

    fbi->fbops = &fops;
    fbi->screen_base = v_addr; //显存虚拟地址
    fbi->screen_size = LCD_W * LCD_H * 3; //显存大小

    spi_set_drvdata(spi, fbi);
    register_framebuffer(fbi);
    data->thread = kthread_run(thread_func, fbi, spi->modalias);
    event.info = fbi;
    fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);

    return fbi;
}

void fb_del(struct spi_device *spi) //此函数在spi设备驱动remove时被调用
{
    struct fb_info *fbi = spi_get_drvdata(spi);
    lcd_data_t *data = fbi->par;

    kthread_stop(data->thread); //让刷图线程退出
    unregister_framebuffer(fbi);
    dma_free_coherent(NULL, fbi->fix.smem_len, fbi->screen_base, fbi->fix.smem_start);
    framebuffer_release(fbi);
}

在framebuffer驱动注册完成的最后发送一个fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);就切换完成了。

适配显示

终端切换成功了,但是图像没有办法显示,都是乱码。手头的屏幕是控制器是ILI9488,spi传送的图像数据一个像素由三个字节组成,分别是蓝绿红。三个字节并不代表是24位,其中有效数据只占16位,蓝5位,绿6位,红5位。也就是rgb565,奇葩的是这三个颜色在一个字节中还是高位对齐,也就是说低位并没有使用,注意并不是大端对齐模式。为了对齐最后蓝和红也占6位了,最后变成了奇葩的rgb666吧。附上数据手册中的数据结构图
在这里插入图片描述
这种摆放方式不知道内核中有没有,软件支持性也不清楚。索性直接转换吧。为了使绘图效率达到最高,就不在spi传输的时候做转换了,而是在写入fb缓冲内存的时候就吧格式摆放正确。先做一下虚拟终端的绘图部分。虚拟终端主要调用fb_ops结构体中的几个函数完成字符绘制,我们只需要重写其中两个:fb_imageblit fb_fillrectfb_imageblit是绘制文本的主要函数,他接收矢量字符,坐标,向fb缓冲区绘图。

fb_imageblit

适量字符每一位代表一个像素,比如Z的适量字符:

	/* 90 0x5a 'Z' */
	0x00, 0x00, /* 0000000000 */
	0x3f, 0x80, /* 0011111110 */
	0x21, 0x80, /* 0010000110 */
	0x01, 0x80, /* 0000000110 */
	0x03, 0x00, /* 0000001100 */
	0x03, 0x00, /* 0000001100 */
	0x06, 0x00, /* 0000011000 */
	0x06, 0x00, /* 0000011000 */
	0x0c, 0x00, /* 0000110000 */
	0x0c, 0x00, /* 0000110000 */
	0x18, 0x00, /* 0001100000 */
	0x18, 0x00, /* 0001100000 */
	0x30, 0x00, /* 0011000000 */
	0x30, 0x80, /* 0011000010 */
	0x3f, 0x80, /* 0011111110 */
	0x00, 0x00, /* 0000000000 */
	0x00, 0x00, /* 0000000000 */
	0x00, 0x00, /* 0000000000 */

根据矢量字符以我们的特殊rgb666绘图:

void fb_imageblit(struct fb_info *info, const struct fb_image *image) {
    unsigned char *p, *p2, shift, *src;
    u32 dx, dy, width, height, bpp, i, j;
    dx = image->dx;
    dy = image->dy;
    width = image->width;
    height = image->height;
    bpp = info->var.bits_per_pixel;
    src = image->data;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        shift = 8;
        for (j = 0; j < width; ++j)
        {
            shift--;
            if (*src & (1 << shift)) {
                p2[0] = 0xf8;
                p2[1] = 0xf8;
                p2[2] = 0xf8;
            } else {
                p2[0] = 0x00;
                p2[1] = 0x00;
                p2[2] = 0x00;
            }
            if (!shift) { shift = 8; src++; };
            p2 += 3;
        }
        p += info->fix.line_length;
    }
}
fb_fillrect

fb_fillrect是清除字符调用的函数,在需要的区域填充黑色即可。

static void fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
    unsigned char *p, *p2;
    u32 dx, dy, width, height, bpp, i, j;
    dx = rect->dx;
    dy = rect->dy;
    width = rect->width;
    height = rect->height;
    bpp = info->var.bits_per_pixel;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        for (j = 0; j < width; ++j)
        {
                p2[0] = 0x00;
                p2[1] = 0xf8;
                p2[2] = 0x00;
                p2 += 3;
        }
        p += info->fix.line_length;
    }
}
整体代码

framebuffer.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/spi/spi.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include "fb_spi.h"

typedef struct {
    struct spi_device *spi;
    struct task_struct *thread;
} lcd_data_t;

static void fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
    unsigned char *p, *p2;
    u32 dx, dy, width, height, bpp, i, j;
    dx = rect->dx;
    dy = rect->dy;
    width = rect->width;
    height = rect->height;
    bpp = info->var.bits_per_pixel;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        for (j = 0; j < width; ++j)
        {
                p2[0] = 0x00;
                p2[1] = 0xf8;
                p2[2] = 0x00;
                p2 += 3;
        }
        p += info->fix.line_length;
    }
}

void fb_imageblit(struct fb_info *info, const struct fb_image *image) {
    unsigned char *p, *p2, shift, *src;
    u32 dx, dy, width, height, bpp, i, j;
    dx = image->dx;
    dy = image->dy;
    width = image->width;
    height = image->height;
    bpp = info->var.bits_per_pixel;
    src = image->data;

    p = info->screen_base + info->fix.line_length * dy + dx * bpp;
    for (i = 0; i < height; ++i)
    {
        p2 = p;
        shift = 8;
        for (j = 0; j < width; ++j)
        {
            shift--;
            if (*src & (1 << shift)) {
                p2[0] = 0xf8;
                p2[1] = 0xf8;
                p2[2] = 0xf8;
            } else {
                p2[0] = 0x00;
                p2[1] = 0x00;
                p2[2] = 0x00;
            }
            if (!shift) { shift = 8; src++; };
            p2 += 3;
        }
        p += info->fix.line_length;
    }
}

struct fb_ops fops = {
    .owner          = THIS_MODULE,
    .fb_imageblit   = fb_imageblit,
    .fb_fillrect = fb_fillrect,
};

/* from pxafb.c */
static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
    chan &= 0xffff;
    chan >>= 16 - bf->length;
    return chan << bf->offset;
}

extern void show_fb(struct fb_info *fbi, struct spi_device *spi);
int thread_func(void *data)
{
    struct fb_info *fbi = (struct fb_info *)data;
    lcd_data_t *ldata = fbi->par;

    while (1)
    {
        if (kthread_should_stop())
            break;
        show_fb(fbi, ldata->spi);
    }

    return 0;
}

struct fb_info * fb_init(struct spi_device *spi) //此函数在spi设备驱动的probe函数里被调用
{
    struct fb_info *fbi;
    u8 *v_addr;
    u32 p_addr;
    lcd_data_t *data;
    struct fb_event event;

    v_addr = dma_alloc_coherent(NULL, LCD_W * LCD_H * 3, &p_addr, GFP_KERNEL);

    //额外分配lcd_data_t类型空间
    fbi = framebuffer_alloc(sizeof(lcd_data_t), NULL);
    data = fbi->par; //data指针指向额外分配的空间

    data->spi = spi;

    fbi->var.xres = LCD_W;
    fbi->var.yres = LCD_H;
    fbi->var.xres_virtual = LCD_W;
    fbi->var.yres_virtual = LCD_H;
    fbi->var.bits_per_pixel = 24;
    fbi->var.red.offset = 16;
    fbi->var.red.length = 5;
    fbi->var.green.offset = 8;
    fbi->var.green.length = 6;
    fbi->var.blue.offset = 0;
    fbi->var.blue.length = 5;
    // fbi->var.pixclock   = 93006;
    fbi->var.activate    = FB_ACTIVATE_NOW;

    strcpy(fbi->fix.id, "hello_fb");
    fbi->fix.smem_start = p_addr; //显存的物理地址
    fbi->fix.smem_len = LCD_W * LCD_H * 3;
    fbi->fix.type = FB_TYPE_PACKED_PIXELS;
    fbi->fix.visual = FB_VISUAL_TRUECOLOR;
    fbi->fix.line_length = LCD_W * 3;

    fbi->fbops = &fops;
    fbi->screen_base = v_addr; //显存虚拟地址
    fbi->screen_size = LCD_W * LCD_H * 3; //显存大小

    spi_set_drvdata(spi, fbi);
    register_framebuffer(fbi);
    data->thread = kthread_run(thread_func, fbi, spi->modalias);
    event.info = fbi;
    fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);

    return fbi;
}

void fb_del(struct spi_device *spi) //此函数在spi设备驱动remove时被调用
{
    struct fb_info *fbi = spi_get_drvdata(spi);
    lcd_data_t *data = fbi->par;

    kthread_stop(data->thread); //让刷图线程退出
    unregister_framebuffer(fbi);
    dma_free_coherent(NULL, fbi->fix.smem_len, fbi->screen_base, fbi->fix.smem_start);
    framebuffer_release(fbi);
}

spi.h


struct  Lcd_dev
{										    
	unsigned short width;			//LCD 宽度
	unsigned short height;			//LCD 高度
	unsigned short id;				//LCD ID
	unsigned char  dir;			//横屏还是竖屏控制:0,竖屏;1,横屏。	
	unsigned short	 wramcmd;		//开始写gram指令
	unsigned short  setxcmd;		//设置x坐标指令
	unsigned short  setycmd;		//设置y坐标指令	 
};

//画笔颜色
#define WHITE            0xFFFF
#define BLACK            0x0000   
#define BLUE           0x001F  
#define BRED             0XF81F
#define GRED       0XFFE0
#define GBLUE      0X07FF
#define RED              0xF800
#define MAGENTA          0xF81F
#define GREEN            0x07E0
#define CYAN             0x7FFF
#define YELLOW           0xFFE0
#define BROWN        0XBC40 //棕色
#define BRRED        0XFC07 //棕红色
#define GRAY         0X8430 //灰色
//GUI颜色

#define DARKBLUE         0X01CF //深蓝色
#define LIGHTBLUE        0X7D7C //浅蓝色  
#define GRAYBLUE         0X5458 //灰蓝色
//以上三色为PANEL的颜色 
 
#define LIGHTGREEN       0X841F //浅绿色
#define LGRAY        0XC618 //浅灰色(PANNEL),窗体背景色

#define LGRAYBLUE        0XA651 //浅灰蓝色(中间层颜色)
#define LBBLUE           0X2B12 //浅棕蓝色(选择条目的反色)

//LCD的画笔颜色和背景色	   
#define POINT_COLOR (0x0000)	//画笔颜色
#define BACK_COLOR (0xFFFF)		//背景色
//定义LCD的尺寸
#define LCD_W 320
#define LCD_H 480

#define USE_HORIZONTAL  	0	//定义液晶屏顺时针旋转方向 	0-0度旋转,1-90度旋转,2-180度旋转,3-270度旋转

spi.c


#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/fb.h>
#include "fb_dev.h"
#include "fb_spi.h"

struct spi_lcd_cmd {
    u8  d_or_r;
    u8  data; // command
    int delay_ms; //此命令发送数据完成后,需延时多久
} cmds[] = {
    {0, 0XF7, 0},
    {1, 0xA9, 0},
    {1, 0x51, 0},
    {1, 0x2C, 0},
    {1, 0x82, 0},
    {0, 0xC0, 0},
    {1, 0x11, 0},
    {1, 0x09, 0},
    {0, 0xC1, 0},
    {1, 0x41, 0},
    {0, 0XC5, 0},
    {1, 0x00, 0},
    {1, 0x0A, 0},
    {1, 0x80, 0},
    {0, 0xB1, 0},
    {1, 0xB0, 0},
    {1, 0x11, 0},
    {0, 0xB4, 0},
    {1, 0x02, 0},
    {0, 0xB6, 0},
    {1, 0x02, 0},
    {1, 0x42, 0},
    {0, 0xB7, 0},
    {1, 0xc6, 0},
    {0, 0xBE, 0},
    {1, 0x00, 0},
    {1, 0x04, 0},
    {0, 0xE9, 0},
    {1, 0x00, 0},
    {0, 0x36, 0},
    {1, (1 << 3) | (0 << 7) | (1 << 6) | (1 << 5), 0},
    {0, 0x3A, 0},
    {1, 0x66, 0},
    {0, 0xE0, 0},
    {1, 0x00, 0},
    {1, 0x07, 0},
    {1, 0x10, 0},
    {1, 0x09, 0},
    {1, 0x17, 0},
    {1, 0x0B, 0},
    {1, 0x41, 0},
    {1, 0x89, 0},
    {1, 0x4B, 0},
    {1, 0x0A, 0},
    {1, 0x0C, 0},
    {1, 0x0E, 0},
    {1, 0x18, 0},
    {1, 0x1B, 0},
    {1, 0x0F, 0},
    {0, 0XE1, 0},
    {1, 0x00, 0},
    {1, 0x17, 0},
    {1, 0x1A, 0},
    {1, 0x04, 0},
    {1, 0x0E, 0},
    {1, 0x06, 0},
    {1, 0x2F, 0},
    {1, 0x45, 0},
    {1, 0x43, 0},
    {1, 0x02, 0},
    {1, 0x0A, 0},
    {1, 0x09, 0},
    {1, 0x32, 0},
    {1, 0x36, 0},
    {1, 0x0F, 0},
    {0, 0x11, 0},
    {0, 0x29, 0},
};

struct  Lcd_dev lcddev;

static inline void write_u8(struct spi_device * spi, u8 d_or_r, u8 cmd)
{
    struct fb_data *pdata = spi->dev.platform_data;
    gpio_set_value(pdata->rs, d_or_r);
    spi_write(spi, &cmd, 1);
}

static void write_u24s(struct spi_device * spi, u8 * data, unsigned int len)
{
    struct fb_data *pdata = spi->dev.platform_data;
    gpio_set_value(pdata->rs, 1);
    spi_write(spi, data, len);
}

void lcd_set_window(struct spi_device * spi, unsigned short xStar, unsigned short yStar, unsigned short xEnd, unsigned short yEnd)
{
    write_u8(spi, 0, lcddev.setxcmd);
    write_u8(spi, 1, xStar >> 8);
    write_u8(spi, 1, 0x00FF & xStar);
    write_u8(spi, 1, xEnd >> 8);
    write_u8(spi, 1, 0x00FF & xEnd);

    write_u8(spi, 0, lcddev.setycmd);
    write_u8(spi, 1, yStar >> 8);
    write_u8(spi, 1, 0x00FF & yStar);
    write_u8(spi, 1, yEnd >> 8);
    write_u8(spi, 1, 0x00FF & yEnd);

    write_u8(spi, 0, lcddev.wramcmd);
}

void lcd_clear(struct spi_device * spi, struct fb_info * fbi, unsigned short Color)
{
    unsigned int i;
    unsigned char *p;
    lcd_set_window(spi, 0, 0, lcddev.width - 1, lcddev.height - 1);
    p = fbi->screen_base;
    for (i = 0; i < fbi->var.xres * fbi->var.yres; i++)
    {
        p[i * 3] = (Color >> 8) & 0xF8;
        p[i * 3 + 1] = (Color >> 3) & 0xFC;
        p[i * 3 + 2] = (Color << 3);
    }
}

void lcd_direction(struct spi_device * spi, unsigned char direction)
{
    lcddev.setxcmd = 0x2A;
    lcddev.setycmd = 0x2B;
    lcddev.wramcmd = 0x2C;
    switch (direction) {
    case 0:
        lcddev.width = LCD_W;
        lcddev.height = LCD_H;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (0 << 6) | (0 << 7)); //BGR==1,MY==0,MX==0,MV==0
        break;
    case 1:
        lcddev.width = LCD_H;
        lcddev.height = LCD_W;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (0 << 7) | (1 << 6) | (1 << 5)); //BGR==1,MY==1,MX==0,MV==1
        break;
    case 2:
        lcddev.width = LCD_W;
        lcddev.height = LCD_H;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (1 << 6) | (1 << 7)); //BGR==1,MY==0,MX==0,MV==0
        break;
    case 3:
        lcddev.width = LCD_H;
        lcddev.height = LCD_W;
        write_u8(spi, 0, 0x36);
        write_u8(spi, 1, (1 << 3) | (1 << 7) | (1 << 5)); //BGR==1,MY==1,MX==0,MV==1
        break;
    default: break;
    }
}

//初始化spi_lcd
static void spi_lcd_init(struct spi_device * spi)
{
    struct fb_data *pdata = spi->dev.platform_data;
    int i, n;

    // 屏复位
    gpio_set_value(pdata->reset, 0);
    mdelay(100);
    gpio_set_value(pdata->reset, 1);
    mdelay(100);

    n = 0; // n用于记录数据数组spi_lcd_datas的位置
    //发命令,并发出命令所需的数据
    for (i = 0; i < ARRAY_SIZE(cmds); i++) //命令
    {
        write_u8(spi, cmds[i].d_or_r, cmds[i].data);
        if (cmds[i].delay_ms)
            mdelay(cmds[i].delay_ms);
    }
}

void show_fb(struct fb_info * fbi, struct spi_device * spi)
{
    u8 *p = (u8 *)(fbi->screen_base);

    lcd_set_window(spi, 0, 0, lcddev.width - 1, lcddev.height - 1); //从屏的0,0坐标开始刷
    write_u24s(spi, p, fbi->screen_size/2);
    write_u24s(spi, p + fbi->screen_size/2, fbi->screen_size/2);
}

extern struct fb_info * fb_init(struct spi_device *);
static int spi_probe(struct spi_device * spi)
{
    struct fb_data *pdata = spi->dev.platform_data;
    int ret;
    struct fb_info * fbi;
    fbi = NULL;

    ret = gpio_request(pdata->reset, spi->modalias);
    if (ret < 0)
        goto err0;
    ret = gpio_request(pdata->rs, spi->modalias);
    if (ret < 0)
        goto err1;

    gpio_direction_output(pdata->rs, 0);
    gpio_direction_output(pdata->reset, 1);
    spi_lcd_init(spi); //初始化
    lcd_direction(spi, USE_HORIZONTAL); //设置LCD显示方向
    fbi = fb_init(spi); //fb设备初始化
    // lcd_clear(spi, fbi, BLUE);

    printk("probe ...%s\n", spi->modalias);
    return 0;

err1:
    gpio_free(pdata->reset);
err0:
    return ret;
}

extern void fb_del(struct spi_device *);
static int spi_remove(struct spi_device * spi)
{
    struct fb_data *pdata = spi->dev.platform_data;

    fb_del(spi); //fb设备回收

    gpio_free(pdata->rs);
    gpio_free(pdata->reset);
    printk("%s remove\n", spi->modalias);
    return 0;
}

static struct spi_driver spi_drv = {
    .driver = {
        .owner = THIS_MODULE,
        .name = "hello_spi_fb",
    },
    .probe = spi_probe,
    .remove = spi_remove,
};

static int __init hello_init(void) {
    int ret;
    ret = spi_register_driver(&spi_drv);
    if (ret) {
        printk("hello driver fail\n");
    } else {
        printk("hello driver init\n");
    }
    return ret;
}

static void __exit hello_exit(void) {
    spi_unregister_driver(&spi_drv);
    printk("hello device exit\n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

dev.h

#ifndef __FB_DEV__
#define __FB_DEV__

struct fb_data{
	int reset;
	int rs;	
};
#endif

dev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <mach/gpio.h>
#include <mach/platform.h>
#include <linux/spi/spi.h>
#include <linux/amba/pl022.h>
#include "fb_dev.h"

static struct fb_data fb1_plat_data = {
	.reset  = PAD_GPIO_D + 21,
	.rs    = PAD_GPIO_D + 17,
};

struct pl022_config_chip spi0_info = {
	/* available POLLING_TRANSFER, INTERRUPT_TRANSFER, DMA_TRANSFER */
	.com_mode = CFG_SPI0_COM_MODE,
	.iface = SSP_INTERFACE_MOTOROLA_SPI,
	/* We can only act as master but SSP_SLAVE is possible in theory */
	.hierarchy = SSP_MASTER,
	/* 0 = drive TX even as slave, 1 = do not drive TX as slave */
	.slave_tx_disable = 1,
	.rx_lev_trig = SSP_RX_4_OR_MORE_ELEM,
	.tx_lev_trig = SSP_TX_4_OR_MORE_EMPTY_LOC,
	.ctrl_len = SSP_BITS_8,
	.wait_state = SSP_MWIRE_WAIT_ZERO,
	.duplex = SSP_MICROWIRE_CHANNEL_FULL_DUPLEX,
	.clkdelay = SSP_FEEDBACK_CLK_DELAY_1T,
};

static struct spi_board_info spi_plat_board = {
	.modalias        = "hello_spi_fb",    /* fixup */
	.max_speed_hz    = 25000000,     /* max spi clock (SCK) speed in HZ */
	.bus_num         = 0,           /* Note> set bus num, must be smaller than ARRAY_SIZE(spi_plat_device) */
	.chip_select     = 1,           /* Note> set chip select num, must be smaller than spi cs_num */
	.controller_data = &spi0_info,
	.mode            = SPI_MODE_3 | SPI_CPOL | SPI_CPHA,
	.platform_data   = &fb1_plat_data,
};

__attribute__ ((unused)) static void device_spi_delete(struct spi_master *master, unsigned cs)
{
	struct device *dev;
	char str[32];

	snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), cs);

	dev = bus_find_device_by_name(&spi_bus_type, NULL, str);
	if (dev) {
		printk(": Deleting %s\n", str);
		device_del(dev);
	}
}

static struct spi_device *spi_device;
static int __init hello_init(void) {
	struct spi_master *master;

	master = spi_busnum_to_master(spi_plat_board.bus_num);
	if (!master) {
		printk(":  spi_busnum_to_master(%d) returned NULL\n",
		       spi_plat_board.bus_num);
		return -EINVAL;
	}
	/* make sure it's available */
	// device_spi_delete(master, spi_plat_board.chip_select);
	spi_device = spi_new_device(master, &spi_plat_board);
	put_device(&master->dev);
	if (!spi_device) {
		printk(":    spi_new_device() returned NULL\n");
		return -EPERM;
	}
	return 0;
	printk("hello device init\n");
	return 0;
}

static void __exit hello_exit(void) {
	if (spi_device) {
		if (spi_device->master->cleanup) {
			spi_device->master->cleanup(spi_device);
		}
		device_del(&spi_device->dev);
		kfree(spi_device);
	}
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

运行效果

在这里插入图片描述

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值