Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   2)  * linux/drivers/video/hecubafb.c -- FB driver for Hecuba/Apollo controller
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   3)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   4)  * Copyright (C) 2006, Jaya Kumar
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   5)  * This work was sponsored by CIS(M) Sdn Bhd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   6)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   7)  * This file is subject to the terms and conditions of the GNU General Public
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   8)  * License. See the file COPYING in the main directory of this archive for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300   9)  * more details.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  10)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  11)  * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  12)  * This work was possible because of apollo display code from E-Ink's website
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  13)  * http://support.eink.com/community
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  14)  * All information used to write this code is from public material made
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  15)  * available by E-Ink on its support site. Some commands such as 0xA4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  16)  * were found by looping through cmd=0x00 thru 0xFF and supplying random
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  17)  * values. There are other commands that the display is capable of,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  18)  * beyond the 5 used here but they are more complex.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  19)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  20)  * This driver is written to be used with the Hecuba display architecture.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  21)  * The actual display chip is called Apollo and the interface electronics
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  22)  * it needs is called Hecuba.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  23)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  24)  * It is intended to be architecture independent. A board specific driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  25)  * must be used to perform all the physical IO interactions. An example
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  26)  * is provided as n411.c
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  27)  *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  28)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  29) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  30) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  31) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  32) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  33) #include <linux/string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  34) #include <linux/mm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  35) #include <linux/vmalloc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  36) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  37) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  38) #include <linux/fb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  39) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  40) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  41) #include <linux/list.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  42) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  43) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  44) #include <video/hecubafb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  45) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  46) /* Display specific information */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  47) #define DPY_W 600
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  48) #define DPY_H 800
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  49) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  50) static const struct fb_fix_screeninfo hecubafb_fix = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  51) 	.id =		"hecubafb",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  52) 	.type =		FB_TYPE_PACKED_PIXELS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  53) 	.visual =	FB_VISUAL_MONO01,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  54) 	.xpanstep =	0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  55) 	.ypanstep =	0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  56) 	.ywrapstep =	0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  57) 	.line_length =	DPY_W,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  58) 	.accel =	FB_ACCEL_NONE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  59) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  60) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  61) static const struct fb_var_screeninfo hecubafb_var = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  62) 	.xres		= DPY_W,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  63) 	.yres		= DPY_H,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  64) 	.xres_virtual	= DPY_W,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  65) 	.yres_virtual	= DPY_H,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  66) 	.bits_per_pixel	= 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  67) 	.nonstd		= 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  68) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  69) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  70) /* main hecubafb functions */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  71) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  72) static void apollo_send_data(struct hecubafb_par *par, unsigned char data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  73) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  74) 	/* set data */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  75) 	par->board->set_data(par, data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  76) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  77) 	/* set DS low */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  78) 	par->board->set_ctl(par, HCB_DS_BIT, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  79) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  80) 	/* wait for ack */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  81) 	par->board->wait_for_ack(par, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  82) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  83) 	/* set DS hi */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  84) 	par->board->set_ctl(par, HCB_DS_BIT, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  85) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  86) 	/* wait for ack to clear */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  87) 	par->board->wait_for_ack(par, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  88) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  89) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  90) static void apollo_send_command(struct hecubafb_par *par, unsigned char data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  91) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  92) 	/* command so set CD to high */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  93) 	par->board->set_ctl(par, HCB_CD_BIT, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  94) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  95) 	/* actually strobe with command */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  96) 	apollo_send_data(par, data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  97) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  98) 	/* clear CD back to low */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300  99) 	par->board->set_ctl(par, HCB_CD_BIT, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) static void hecubafb_dpy_update(struct hecubafb_par *par)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) 	int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) 	unsigned char *buf = (unsigned char __force *)par->info->screen_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) 	apollo_send_command(par, APOLLO_START_NEW_IMG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) 	for (i=0; i < (DPY_W*DPY_H/8); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) 		apollo_send_data(par, *(buf++));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) 	apollo_send_command(par, APOLLO_STOP_IMG_DATA);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) 	apollo_send_command(par, APOLLO_DISPLAY_IMG);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) /* this is called back from the deferred io workqueue */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) static void hecubafb_dpy_deferred_io(struct fb_info *info,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) 				struct list_head *pagelist)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) 	hecubafb_dpy_update(info->par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) static void hecubafb_fillrect(struct fb_info *info,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) 				   const struct fb_fillrect *rect)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) 	struct hecubafb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) 	sys_fillrect(info, rect);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) 	hecubafb_dpy_update(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) static void hecubafb_copyarea(struct fb_info *info,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) 				   const struct fb_copyarea *area)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) 	struct hecubafb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) 	sys_copyarea(info, area);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) 	hecubafb_dpy_update(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) static void hecubafb_imageblit(struct fb_info *info,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) 				const struct fb_image *image)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) 	struct hecubafb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) 	sys_imageblit(info, image);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) 	hecubafb_dpy_update(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)  * this is the slow path from userspace. they can seek and write to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)  * the fb. it's inefficient to do anything less than a full screen draw
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)  */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) static ssize_t hecubafb_write(struct fb_info *info, const char __user *buf,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) 				size_t count, loff_t *ppos)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) 	struct hecubafb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) 	unsigned long p = *ppos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) 	void *dst;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) 	int err = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) 	unsigned long total_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) 	if (info->state != FBINFO_STATE_RUNNING)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) 		return -EPERM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) 	total_size = info->fix.smem_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) 	if (p > total_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) 		return -EFBIG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) 	if (count > total_size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) 		err = -EFBIG;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) 		count = total_size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) 	if (count + p > total_size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) 		if (!err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) 			err = -ENOSPC;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) 		count = total_size - p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) 	dst = (void __force *) (info->screen_base + p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) 	if (copy_from_user(dst, buf, count))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) 		err = -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) 	if  (!err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) 		*ppos += count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) 	hecubafb_dpy_update(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) 	return (err) ? err : count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) static const struct fb_ops hecubafb_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) 	.owner		= THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) 	.fb_read        = fb_sys_read,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) 	.fb_write	= hecubafb_write,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) 	.fb_fillrect	= hecubafb_fillrect,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) 	.fb_copyarea	= hecubafb_copyarea,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) 	.fb_imageblit	= hecubafb_imageblit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) static struct fb_deferred_io hecubafb_defio = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) 	.delay		= HZ,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) 	.deferred_io	= hecubafb_dpy_deferred_io,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) static int hecubafb_probe(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) 	struct fb_info *info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) 	struct hecuba_board *board;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) 	int retval = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) 	int videomemorysize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) 	unsigned char *videomemory;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) 	struct hecubafb_par *par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) 	/* pick up board specific routines */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) 	board = dev->dev.platform_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) 	if (!board)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) 		return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) 	/* try to count device specific driver, if can't, platform recalls */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) 	if (!try_module_get(board->owner))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) 		return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) 	videomemorysize = (DPY_W*DPY_H)/8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) 	videomemory = vzalloc(videomemorysize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) 	if (!videomemory)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) 		goto err_videomem_alloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) 	info = framebuffer_alloc(sizeof(struct hecubafb_par), &dev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) 	if (!info)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) 		goto err_fballoc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) 	info->screen_base = (char __force __iomem *)videomemory;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) 	info->fbops = &hecubafb_ops;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) 	info->var = hecubafb_var;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) 	info->fix = hecubafb_fix;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) 	info->fix.smem_len = videomemorysize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) 	par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) 	par->info = info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) 	par->board = board;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) 	par->send_command = apollo_send_command;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) 	par->send_data = apollo_send_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) 	info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) 	info->fbdefio = &hecubafb_defio;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) 	fb_deferred_io_init(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) 	retval = register_framebuffer(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) 	if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) 		goto err_fbreg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) 	platform_set_drvdata(dev, info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) 	fb_info(info, "Hecuba frame buffer device, using %dK of video memory\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) 		videomemorysize >> 10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) 	/* this inits the dpy */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) 	retval = par->board->init(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) 	if (retval < 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) 		goto err_fbreg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) err_fbreg:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) 	framebuffer_release(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) err_fballoc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) 	vfree(videomemory);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) err_videomem_alloc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) 	module_put(board->owner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) 	return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) static int hecubafb_remove(struct platform_device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) 	struct fb_info *info = platform_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) 	if (info) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) 		struct hecubafb_par *par = info->par;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) 		fb_deferred_io_cleanup(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) 		unregister_framebuffer(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) 		vfree((void __force *)info->screen_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) 		if (par->board->remove)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) 			par->board->remove(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) 		module_put(par->board->owner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) 		framebuffer_release(info);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) 	}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) 	return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) static struct platform_driver hecubafb_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) 	.probe	= hecubafb_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) 	.remove = hecubafb_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) 	.driver	= {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) 		.name	= "hecubafb",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) 	},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) module_platform_driver(hecubafb_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) 
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) MODULE_DESCRIPTION("fbdev driver for Hecuba/Apollo controller");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) MODULE_AUTHOR("Jaya Kumar");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) MODULE_LICENSE("GPL");