📄 hwa742.c
字号:
/* * Epson HWA742 LCD controller driver * * Copyright (C) 2004-2005 Nokia Corporation * Authors: Juha Yrjölä <juha.yrjola@nokia.com> * Imre Deak <imre.deak@nokia.com> * YUV support: Jussi Laako <jussi.laako@nokia.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. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/module.h>#include <linux/mm.h>#include <linux/fb.h>#include <linux/delay.h>#include <linux/clk.h>#include <mach/dma.h>#include <mach/omapfb.h>#include <mach/hwa742.h>#define HWA742_REV_CODE_REG 0x0#define HWA742_CONFIG_REG 0x2#define HWA742_PLL_DIV_REG 0x4#define HWA742_PLL_0_REG 0x6#define HWA742_PLL_1_REG 0x8#define HWA742_PLL_2_REG 0xa#define HWA742_PLL_3_REG 0xc#define HWA742_PLL_4_REG 0xe#define HWA742_CLK_SRC_REG 0x12#define HWA742_PANEL_TYPE_REG 0x14#define HWA742_H_DISP_REG 0x16#define HWA742_H_NDP_REG 0x18#define HWA742_V_DISP_1_REG 0x1a#define HWA742_V_DISP_2_REG 0x1c#define HWA742_V_NDP_REG 0x1e#define HWA742_HS_W_REG 0x20#define HWA742_HP_S_REG 0x22#define HWA742_VS_W_REG 0x24#define HWA742_VP_S_REG 0x26#define HWA742_PCLK_POL_REG 0x28#define HWA742_INPUT_MODE_REG 0x2a#define HWA742_TRANSL_MODE_REG1 0x2e#define HWA742_DISP_MODE_REG 0x34#define HWA742_WINDOW_TYPE 0x36#define HWA742_WINDOW_X_START_0 0x38#define HWA742_WINDOW_X_START_1 0x3a#define HWA742_WINDOW_Y_START_0 0x3c#define HWA742_WINDOW_Y_START_1 0x3e#define HWA742_WINDOW_X_END_0 0x40#define HWA742_WINDOW_X_END_1 0x42#define HWA742_WINDOW_Y_END_0 0x44#define HWA742_WINDOW_Y_END_1 0x46#define HWA742_MEMORY_WRITE_LSB 0x48#define HWA742_MEMORY_WRITE_MSB 0x49#define HWA742_MEMORY_READ_0 0x4a#define HWA742_MEMORY_READ_1 0x4c#define HWA742_MEMORY_READ_2 0x4e#define HWA742_POWER_SAVE 0x56#define HWA742_NDP_CTRL 0x58#define HWA742_AUTO_UPDATE_TIME (HZ / 20)/* Reserve 4 request slots for requests in irq context */#define REQ_POOL_SIZE 24#define IRQ_REQ_POOL_SIZE 4#define REQ_FROM_IRQ_POOL 0x01#define REQ_COMPLETE 0#define REQ_PENDING 1struct update_param { int x, y, width, height; int color_mode; int flags;};struct hwa742_request { struct list_head entry; unsigned int flags; int (*handler)(struct hwa742_request *req); void (*complete)(void *data); void *complete_data; union { struct update_param update; struct completion *sync; } par;};struct { enum omapfb_update_mode update_mode; enum omapfb_update_mode update_mode_before_suspend; struct timer_list auto_update_timer; int stop_auto_update; struct omapfb_update_window auto_update_window; unsigned te_connected:1; unsigned vsync_only:1; struct hwa742_request req_pool[REQ_POOL_SIZE]; struct list_head pending_req_list; struct list_head free_req_list; struct semaphore req_sema; spinlock_t req_lock; struct extif_timings reg_timings, lut_timings; int prev_color_mode; int prev_flags; int window_type; u32 max_transmit_size; u32 extif_clk_period; unsigned long pix_tx_time; unsigned long line_upd_time; struct omapfb_device *fbdev; struct lcd_ctrl_extif *extif; struct lcd_ctrl *int_ctrl; void (*power_up)(struct device *dev); void (*power_down)(struct device *dev);} hwa742;struct lcd_ctrl hwa742_ctrl;static u8 hwa742_read_reg(u8 reg){ u8 data; hwa742.extif->set_bits_per_cycle(8); hwa742.extif->write_command(®, 1); hwa742.extif->read_data(&data, 1); return data;}static void hwa742_write_reg(u8 reg, u8 data){ hwa742.extif->set_bits_per_cycle(8); hwa742.extif->write_command(®, 1); hwa742.extif->write_data(&data, 1);}static void set_window_regs(int x_start, int y_start, int x_end, int y_end){ u8 tmp[8]; u8 cmd; x_end--; y_end--; tmp[0] = x_start; tmp[1] = x_start >> 8; tmp[2] = y_start; tmp[3] = y_start >> 8; tmp[4] = x_end; tmp[5] = x_end >> 8; tmp[6] = y_end; tmp[7] = y_end >> 8; hwa742.extif->set_bits_per_cycle(8); cmd = HWA742_WINDOW_X_START_0; hwa742.extif->write_command(&cmd, 1); hwa742.extif->write_data(tmp, 8);}static void set_format_regs(int conv, int transl, int flags){ if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) { hwa742.window_type = ((hwa742.window_type & 0xfc) | 0x01);#ifdef VERBOSE dev_dbg(hwa742.fbdev->dev, "hwa742: enabled pixel doubling\n");#endif } else { hwa742.window_type = (hwa742.window_type & 0xfc);#ifdef VERBOSE dev_dbg(hwa742.fbdev->dev, "hwa742: disabled pixel doubling\n");#endif } hwa742_write_reg(HWA742_INPUT_MODE_REG, conv); hwa742_write_reg(HWA742_TRANSL_MODE_REG1, transl); hwa742_write_reg(HWA742_WINDOW_TYPE, hwa742.window_type);}static void enable_tearsync(int y, int width, int height, int screen_height, int force_vsync){ u8 b; b = hwa742_read_reg(HWA742_NDP_CTRL); b |= 1 << 2; hwa742_write_reg(HWA742_NDP_CTRL, b); if (likely(hwa742.vsync_only || force_vsync)) { hwa742.extif->enable_tearsync(1, 0); return; } if (width * hwa742.pix_tx_time < hwa742.line_upd_time) { hwa742.extif->enable_tearsync(1, 0); return; } if ((width * hwa742.pix_tx_time / 1000) * height < (y + height) * (hwa742.line_upd_time / 1000)) { hwa742.extif->enable_tearsync(1, 0); return; } hwa742.extif->enable_tearsync(1, y + 1);}static void disable_tearsync(void){ u8 b; hwa742.extif->enable_tearsync(0, 0); b = hwa742_read_reg(HWA742_NDP_CTRL); b &= ~(1 << 2); hwa742_write_reg(HWA742_NDP_CTRL, b);}static inline struct hwa742_request *alloc_req(void){ unsigned long flags; struct hwa742_request *req; int req_flags = 0; if (!in_interrupt()) down(&hwa742.req_sema); else req_flags = REQ_FROM_IRQ_POOL; spin_lock_irqsave(&hwa742.req_lock, flags); BUG_ON(list_empty(&hwa742.free_req_list)); req = list_entry(hwa742.free_req_list.next, struct hwa742_request, entry); list_del(&req->entry); spin_unlock_irqrestore(&hwa742.req_lock, flags); INIT_LIST_HEAD(&req->entry); req->flags = req_flags; return req;}static inline void free_req(struct hwa742_request *req){ unsigned long flags; spin_lock_irqsave(&hwa742.req_lock, flags); list_del(&req->entry); list_add(&req->entry, &hwa742.free_req_list); if (!(req->flags & REQ_FROM_IRQ_POOL)) up(&hwa742.req_sema); spin_unlock_irqrestore(&hwa742.req_lock, flags);}static void process_pending_requests(void){ unsigned long flags; spin_lock_irqsave(&hwa742.req_lock, flags); while (!list_empty(&hwa742.pending_req_list)) { struct hwa742_request *req; void (*complete)(void *); void *complete_data; req = list_entry(hwa742.pending_req_list.next, struct hwa742_request, entry); spin_unlock_irqrestore(&hwa742.req_lock, flags); if (req->handler(req) == REQ_PENDING) return; complete = req->complete; complete_data = req->complete_data; free_req(req); if (complete) complete(complete_data); spin_lock_irqsave(&hwa742.req_lock, flags); } spin_unlock_irqrestore(&hwa742.req_lock, flags);}static void submit_req_list(struct list_head *head){ unsigned long flags; int process = 1; spin_lock_irqsave(&hwa742.req_lock, flags); if (likely(!list_empty(&hwa742.pending_req_list))) process = 0; list_splice_init(head, hwa742.pending_req_list.prev); spin_unlock_irqrestore(&hwa742.req_lock, flags); if (process) process_pending_requests();}static void request_complete(void *data){ struct hwa742_request *req = (struct hwa742_request *)data; void (*complete)(void *); void *complete_data; complete = req->complete; complete_data = req->complete_data; free_req(req); if (complete) complete(complete_data); process_pending_requests();}static int send_frame_handler(struct hwa742_request *req){ struct update_param *par = &req->par.update; int x = par->x; int y = par->y; int w = par->width; int h = par->height; int bpp; int conv, transl; unsigned long offset; int color_mode = par->color_mode; int flags = par->flags; int scr_width = hwa742.fbdev->panel->x_res; int scr_height = hwa742.fbdev->panel->y_res;#ifdef VERBOSE dev_dbg(hwa742.fbdev->dev, "x %d y %d w %d h %d scr_width %d " "color_mode %d flags %d\n", x, y, w, h, scr_width, color_mode, flags);#endif switch (color_mode) { case OMAPFB_COLOR_YUV422: bpp = 16; conv = 0x08; transl = 0x25; break; case OMAPFB_COLOR_YUV420: bpp = 12; conv = 0x09; transl = 0x25; break; case OMAPFB_COLOR_RGB565: bpp = 16; conv = 0x01; transl = 0x05; break; default: return -EINVAL; } if (hwa742.prev_flags != flags || hwa742.prev_color_mode != color_mode) { set_format_regs(conv, transl, flags); hwa742.prev_color_mode = color_mode; hwa742.prev_flags = flags; } flags = req->par.update.flags; if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC) enable_tearsync(y, scr_width, h, scr_height, flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC); else disable_tearsync(); set_window_regs(x, y, x + w, y + h); offset = (scr_width * y + x) * bpp / 8; hwa742.int_ctrl->setup_plane(OMAPFB_PLANE_GFX, OMAPFB_CHANNEL_OUT_LCD, offset, scr_width, 0, 0, w, h, color_mode); hwa742.extif->set_bits_per_cycle(16); hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 1); hwa742.extif->transfer_area(w, h, request_complete, req); return REQ_PENDING;}static void send_frame_complete(void *data){ hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 0);}#define ADD_PREQ(_x, _y, _w, _h) do { \ req = alloc_req(); \ req->handler = send_frame_handler; \ req->complete = send_frame_complete; \ req->par.update.x = _x; \ req->par.update.y = _y; \ req->par.update.width = _w; \ req->par.update.height = _h; \ req->par.update.color_mode = color_mode;\ req->par.update.flags = flags; \ list_add_tail(&req->entry, req_head); \} while(0)static void create_req_list(struct omapfb_update_window *win, struct list_head *req_head){ struct hwa742_request *req; int x = win->x; int y = win->y; int width = win->width; int height = win->height; int color_mode; int flags; flags = win->format & ~OMAPFB_FORMAT_MASK; color_mode = win->format & OMAPFB_FORMAT_MASK; if (x & 1) { ADD_PREQ(x, y, 1, height); width--; x++; flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; } if (width & ~1) { unsigned int xspan = width & ~1; unsigned int ystart = y; unsigned int yspan = height; if (xspan * height * 2 > hwa742.max_transmit_size) { yspan = hwa742.max_transmit_size / (xspan * 2); ADD_PREQ(x, ystart, xspan, yspan); ystart += yspan; yspan = height - yspan; flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; } ADD_PREQ(x, ystart, xspan, yspan); x += xspan; width -= xspan; flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC; } if (width) ADD_PREQ(x, y, 1, height);}static void auto_update_complete(void *data){ if (!hwa742.stop_auto_update) mod_timer(&hwa742.auto_update_timer, jiffies + HWA742_AUTO_UPDATE_TIME);}static void hwa742_update_window_auto(unsigned long arg){ LIST_HEAD(req_list); struct hwa742_request *last; create_req_list(&hwa742.auto_update_window, &req_list); last = list_entry(req_list.prev, struct hwa742_request, entry); last->complete = auto_update_complete; last->complete_data = NULL; submit_req_list(&req_list);}int hwa742_update_window_async(struct fb_info *fbi, struct omapfb_update_window *win, void (*complete_callback)(void *arg), void *complete_callback_data){ LIST_HEAD(req_list); struct hwa742_request *last; int r = 0; if (hwa742.update_mode != OMAPFB_MANUAL_UPDATE) { dev_dbg(hwa742.fbdev->dev, "invalid update mode\n"); r = -EINVAL; goto out; } if (unlikely(win->format & ~(0x03 | OMAPFB_FORMAT_FLAG_DOUBLE | OMAPFB_FORMAT_FLAG_TEARSYNC | OMAPFB_FORMAT_FLAG_FORCE_VSYNC))) { dev_dbg(hwa742.fbdev->dev, "invalid window flag\n"); r = -EINVAL; goto out; } create_req_list(win, &req_list); last = list_entry(req_list.prev, struct hwa742_request, entry); last->complete = complete_callback; last->complete_data = (void *)complete_callback_data; submit_req_list(&req_list);out: return r;}EXPORT_SYMBOL(hwa742_update_window_async);static int hwa742_setup_plane(int plane, int channel_out, unsigned long offset, int screen_width, int pos_x, int pos_y, int width, int height, int color_mode){ if (plane != OMAPFB_PLANE_GFX || channel_out != OMAPFB_CHANNEL_OUT_LCD) return -EINVAL; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -