Skip to content

Commit

Permalink
Practice_II_Display: Add framebuffer driver
Browse files Browse the repository at this point in the history
Signed-off-by: Bekir <[email protected]>
  • Loading branch information
BekirovBekir committed Jan 14, 2020
1 parent e6f5e90 commit 8473580
Show file tree
Hide file tree
Showing 10 changed files with 492 additions and 51 deletions.
10 changes: 9 additions & 1 deletion Practice_02-display/lcd_driver/Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
obj-m += lcd_driver.o
TARGET = lcd_module
obj-m += $(TARGET).o
OBJS = lcd_driver.o fb_lcd.o
$(TARGET)-objs = $(OBJS)

KERNEL_DIR := /home/bekir/Work/LinuxKernelCamp/linux/linux-stable

COMPILE_FLAGS += CROSS_COMPILE=arm-linux-gnueabihf- CROSS=arm-linux-gnueabihf- ARCH=arm

all:
make -C $(KERNEL_DIR) M=$(PWD) $(COMPILE_FLAGS) modules

$(TARGET).o: $(OBJS)
ld -r -o $@ $(OBJS)

clean:
make -C $(KERNEL_DIR) M=$(PWD) $(COMPILE_FLAGS) clean

install:
make -C $(KERNEL_DIR) M=$(PWD) modules_install INSTALL_MOD_PATH=$(PWD)/build_modules
155 changes: 155 additions & 0 deletions Practice_02-display/lcd_driver/fb_lcd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* fb_lcd.c
* Author: Bekir Bekirov
* Email: [email protected]
*/

#include "fb_lcd.h"
#include "lcd_driver.h"

static ssize_t st7735sfb_write(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long total_size;
unsigned long p = *ppos;
u8 __iomem *dst;

total_size = info->fix.smem_len;

if (p > total_size)
return -EINVAL;

if (count + p > total_size)
count = total_size - p;

if (!count)
return -EINVAL;

dst = (void __force *) (info->screen_base + p);

if (copy_from_user(dst, buf, count))
return -EFAULT;

lcd_update_screen();

*ppos += count;

return count;
}

static int st7735sfb_blank(int blank_mode, struct fb_info *info)
{
return 0;
}

static void st7735sfb_fillrect(struct fb_info *info,
const struct fb_fillrect *rect)
{
sys_fillrect(info, rect);
lcd_update_screen();
}

static void st7735sfb_copyarea(struct fb_info *info,
const struct fb_copyarea *area)
{
sys_copyarea(info, area);
lcd_update_screen();
}

static void st7735sfb_imageblit(struct fb_info *info,
const struct fb_image *image)
{
lcd_update_screen();
}

static int vfb_mmap(struct fb_info *info,
struct vm_area_struct *vma)
{
return remap_vmalloc_range(vma, (void *)info->fix.smem_start,
vma->vm_pgoff);
}

static struct fb_ops st7735sfb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
.fb_write = st7735sfb_write,
.fb_blank = st7735sfb_blank,
.fb_fillrect = st7735sfb_fillrect,
.fb_copyarea = st7735sfb_copyarea,
.fb_imageblit = st7735sfb_imageblit,
.fb_mmap = vfb_mmap,
};

int init_fb_lcd(struct spi_device *spi)
{
struct lcd_data *ptr_lcddata;
int ret;
struct fb_info *info;
u32 vmem_size;
u8 *vmem;

ptr_lcddata = spi_get_drvdata(spi);
info = framebuffer_alloc(0, &spi->dev);

if (!info)
return -ENOMEM;

pr_info("frame buffer is allocated\n");
ptr_lcddata->info = info;
ptr_lcddata->width = ST7735S_WIDTH;
ptr_lcddata->height = ST7735S_HEIGHT;

vmem = (u8 *)(ptr_lcddata->frame_buffer);
vmem_size = ptr_lcddata->width * ptr_lcddata->height * 2;

info->fbops = &st7735sfb_ops;
info->fix = st7735sfb_fix;
info->fix.line_length = ptr_lcddata->width * 2;

info->var = st7735sfb_var;
info->var.xres = ptr_lcddata->width;
info->var.xres_virtual = ptr_lcddata->width;
info->var.yres = ptr_lcddata->height;
info->var.yres_virtual = ptr_lcddata->height;

info->var.red.length = 5;
info->var.red.offset = 11;
info->var.green.length = 6;
info->var.green.offset = 5;
info->var.blue.length = 5;
info->var.blue.offset = 0;

info->screen_base = (u8 __force __iomem *)vmem;
info->fix.smem_start = __pa(vmem);
info->fix.smem_len = vmem_size;
info->par = NULL;
info->flags = FBINFO_FLAG_DEFAULT;

fb_alloc_cmap(&info->cmap, 256, 0);
ptr_lcddata->info = info;

ret = register_framebuffer(info);
if (ret) {
pr_info("Error: ret = %d\n", ret);
return ret;
}
pr_info("frame buffer is registered\n");
return 0;
}

int deinit_fb_lcd(struct spi_device *spi)
{
struct lcd_data *ptr_lcddata;

ptr_lcddata = spi_get_drvdata(spi);
unregister_framebuffer(ptr_lcddata->info);
fb_dealloc_cmap(&ptr_lcddata->info->cmap);
framebuffer_release(ptr_lcddata->info);

if (!ptr_lcddata)
return -ENODEV;

pr_info("fb is unloaded!\n");

return 0;
}
37 changes: 37 additions & 0 deletions Practice_02-display/lcd_driver/fb_lcd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef FB_LCD_H_
#define FB_LCD_H_

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/uaccess.h>

static struct fb_fix_screeninfo st7735sfb_fix = {
.id = "ST7735S",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.accel = FB_ACCEL_NONE,
};


static struct fb_var_screeninfo st7735sfb_var = {
.bits_per_pixel = 16,
};

#endif /* FB_LCD_H_ */
48 changes: 14 additions & 34 deletions Practice_02-display/lcd_driver/lcd_driver.c
Original file line number Diff line number Diff line change
@@ -1,31 +1,13 @@
#include "lcd_driver.h"

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/uaccess.h>
/*
* lcd_driver.c
* Author: Bekir Bekirov
* Email: [email protected]
*/

#include "lcd_driver.h"

struct lcd_data {
struct spi_device *spi;
struct mutex mutex_sysfs;
struct fb_info *info;
u16 frame_buffer[ST7735S_WIDTH * ST7735S_HEIGHT];
};
extern int init_fb_lcd(struct spi_device *spi);
extern int deinit_fb_lcd(struct spi_device *spi);

static struct lcd_data *lcd_st7735s_data;

Expand Down Expand Up @@ -162,7 +144,7 @@ static void lcd_set_adr_window(u8 x0, u8 y0, u8 x1, u8 y1)
lcd_write_command(ST7735S_RAMWR);
}

inline void lcd_update_screen(void)
void lcd_update_screen(void)
{
lcd_write_data((u8 *)lcd_st7735s_data->frame_buffer,
sizeof(u16) * ST7735S_WIDTH * ST7735S_HEIGHT);
Expand All @@ -171,13 +153,10 @@ inline void lcd_update_screen(void)
void lcd_draw_pixel(u16 x, u16 y, u16 color)
{
if ((x >= ST7735S_WIDTH) || (y >= ST7735S_HEIGHT))
goto out;
return;

lcd_st7735s_data->frame_buffer[x + ST7735S_WIDTH * y] = color;
lcd_update_screen();

out:
return;
}

void lcd_fill_rect(u16 x, u16 y, u16 w, u16 h, u16 color)
Expand All @@ -186,7 +165,7 @@ void lcd_fill_rect(u16 x, u16 y, u16 w, u16 h, u16 color)
u16 j;

if ((x >= ST7735S_WIDTH) || (y >= ST7735S_HEIGHT))
goto out;
return;

if ((x + w - 1) > ST7735S_WIDTH)
w = ST7735S_WIDTH - x;
Expand All @@ -202,9 +181,6 @@ void lcd_fill_rect(u16 x, u16 y, u16 w, u16 h, u16 color)
}

lcd_update_screen();

out:
return;
}

void lcd_fill_screen(u16 color)
Expand Down Expand Up @@ -244,12 +220,16 @@ static int st7735s_probe(struct spi_device *spi)
lcd_fill_rect(34, 60, 60, 40, 0x001f);
lcd_fill_rect(34, 120, 60, 40, 0xf800);

if (init_fb_lcd(spi))
return -EIO;

return 0;
}

static int st7735s_remove(struct spi_device *spi)
{
lcd_st7735s_data = spi_get_drvdata(spi);
deinit_fb_lcd(spi);

if (!lcd_st7735s_data)
return -ENODEV;
Expand Down
29 changes: 29 additions & 0 deletions Practice_02-display/lcd_driver/lcd_driver.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
#ifndef ST7735S_TYPES_H
#define ST7735S_TYPES_H

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/uaccess.h>

#define ST7735S_MADCTL_RGB 0x00
#define ST7735S_MADCTL_BGR 0x08
#define ST7735S_MADCTL_MY 0x80
Expand Down Expand Up @@ -47,4 +65,15 @@

#define ST7735S_DEVICE_NAME "st7735s"

struct lcd_data {
struct spi_device *spi;
struct mutex mutex_sysfs;
struct fb_info *info;
u16 frame_buffer[ST7735S_WIDTH * ST7735S_HEIGHT];
u32 height;
u32 width;
};

void lcd_update_screen(void);

#endif
17 changes: 11 additions & 6 deletions Practice_02-display/logs/dmesg
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
root@orangepione:~# dmesg -w
[ 4452.917936] lcd: device init completed
[ 4453.028007] lcd: sysfs class created
[ 4453.028023] lcd: module loaded
[ 4830.316654] lcd: sysfs class attributes removed
[ 4830.316739] lcd: sysfs class destroyed
[ 4830.316885] lcd: module exited
[21242.825872] lcd: device init completed
[21242.935524] frame buffer is allocated
[21242.936153] Console: switching to colour frame buffer device 16x20
[21243.811276] frame buffer is registered
[21243.811561] lcd: sysfs class created
[21243.811573] lcd: module loaded
[21340.355414] Console: switching to colour dummy device 80x30
[21347.918165] lcd: sysfs class attributes removed
[21347.918248] lcd: sysfs class destroyed
[21347.918553] fb is unloaded!
[21347.918648] lcd: module exited

22 changes: 12 additions & 10 deletions Practice_02-display/logs/log
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
root@orangepione:/home/bekir# insmod lcd_driver.ko
root@orangepione:/home/bekir# echo 0xf800 > /sys/class/lcd_st7735s/fill_screen
root@orangepione:/home/bekir# echo 0x001f > /sys/class/lcd_st7735s/fill_screen
root@orangepione:/home/bekir# echo 0x03e0 > /sys/class/lcd_st7735s/fill_screen
root@orangepione:/home/bekir# echo "30 30 50 50 0x03e0" > /sys/class/lcd_st7735s/fill_rect
root@orangepione:/home/bekir# echo "0x03e0 30 30 50 50" > /sys/class/lcd_st7735s/fill_rect
root@orangepione:/home/bekir# echo 0x0000 > /sys/class/lcd_st7735s/fill_screen
root@orangepione:/home/bekir# echo "0x03e0 30 30 50 50" > /sys/class/lcd_st7735s/fill_rect
root@orangepione:/home/bekir# echo "0x03e0 30 30 50 50" > /sys/class/lcd_st7735s/fill_rect
root@orangepione:/home/bekir# rmmod lcd_driver
root@orangepione:/home/bekir# insmod lcd_module.ko
root@orangepione:/home/bekir# ls /dev/
Display all 642 possibilities? (y or n)
root@orangepione:/home/bekir# ls /dev/f
fb0 fd/ full
root@orangepione:/home/bekir# ls -l /dev/fb0
crw-rw---- 1 root video 29, 0 Dec 19 12:10 /dev/fb0
root@orangepione:/home/bekir# echo 0 > /sys/class/vtconsole/vtcon1/bind
root@orangepione:/home/bekir# rmmod lcd_module
root@orangepione:/home/bekir# ls -l /dev/fb0
ls: cannot access '/dev/fb0': No such file or directory
root@orangepione:/home/bekir#

Binary file not shown.
Loading

0 comments on commit 8473580

Please sign in to comment.