日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

⑥tiny4412 Linux驱动开发之LCD(framebuffer)驱动程序

發(fā)布時(shí)間:2023/12/14 linux 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ⑥tiny4412 Linux驱动开发之LCD(framebuffer)驱动程序 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

友善之臂對(duì)這個(gè)的支持還是比較坑的,我買的開發(fā)板用的是X710屏,我嘞個(gè)去,沒有X710的datasheet,網(wǎng)上也找不到,只能另辟蹊徑了,幸好,友善提供的源代碼里有X710的配置參數(shù),然后也可以順利地顯示出自己定制化的開機(jī)logo.

在說驅(qū)動(dòng)之前,我們先來看一下框架知識(shí),和以往驅(qū)動(dòng)不同的是,這里用到了framebuffer,如下圖是用framebuffer和以往驅(qū)動(dòng)的差異的框架圖:

如上,開發(fā)LCD驅(qū)動(dòng)一般的做法就是上面兩種形式,當(dāng)然,也可以通過塊設(shè)備的方式進(jìn)行開發(fā),這里主要講字符形式的開發(fā),后邊有機(jī)會(huì)的話,用塊設(shè)備驅(qū)動(dòng)的形式做做,然后再分享.

以往字符設(shè)備一般分為3層,驅(qū)動(dòng)控制層,驅(qū)動(dòng)核心層和驅(qū)動(dòng)適配器層.而framebuffer則簡化為了兩層,以方便快速開發(fā),和減少開發(fā)人員的工作量,常見的驅(qū)動(dòng)操作數(shù)據(jù)的形式就是copy_from_user()和copy_to_user(),這樣的形式,可以實(shí)現(xiàn)對(duì)用戶層和驅(qū)動(dòng)層之間的隔離和區(qū)分管控,但是特殊情況則特殊對(duì)待,LCD我們一般應(yīng)用的場景都是播放連續(xù)的畫面,這就是形成目前視頻播放的原理,目前的視頻就是由一張一張連續(xù)的畫面連續(xù)的顯示得到的,一般視頻都是數(shù)據(jù)量比較大,經(jīng)典驅(qū)動(dòng)架構(gòu)copy_from_user()則對(duì)此是低效的方式,因?yàn)橐褦?shù)據(jù)轉(zhuǎn)移兩遍,第一遍是從用戶空間拷貝到內(nèi)核空間,第二遍是從內(nèi)核空間拷貝到物理器件(這里是LCD控制器),這種方式對(duì)于小數(shù)據(jù)量是沒問題的,但是對(duì)于視頻這種大數(shù)據(jù)量時(shí),則會(huì)增加內(nèi)核的負(fù)載和CPU的負(fù)載,因?yàn)檫@種轉(zhuǎn)移是依靠CPU來實(shí)現(xiàn)的,所以為了針對(duì)視頻這種特殊情況,Linux調(diào)整了視頻驅(qū)動(dòng)的架構(gòu),直接把本該內(nèi)核操作的內(nèi)存地址透明給用戶空間,使用戶空間直接給相應(yīng)的內(nèi)存數(shù)據(jù),同時(shí)為了減輕CPU搬運(yùn)數(shù)據(jù)的負(fù)擔(dān),一般SOC都是有集成DMA外設(shè)的,比如我們使用這款exynos 4412就具備,所以,在這里對(duì)于視頻數(shù)據(jù),我們采用映射DMA內(nèi)存的方式搬運(yùn)數(shù)據(jù),以騰出CPU去做別的事情,而這塊被映射的DMA內(nèi)存的視頻緩沖區(qū)則稱之為顯存,我們這里使用的是映射到RAM的顯存,屬于集成顯存,還有一種叫獨(dú)立顯存,比如獨(dú)立顯卡,嵌入式設(shè)備為了節(jié)約成本,一般不用獨(dú)立顯存,同時(shí),也視頻架構(gòu)除了幀緩沖之外,還用一種控制臺(tái)的形式的驅(qū)動(dòng),比如VGA控制臺(tái),這里則只介紹framebuffer幀緩沖.

我們回到實(shí)際開發(fā)中,我先來說一下,這塊開發(fā)的分工:

framebuffer核心層---->Linux內(nèi)核完成(fbmem.c)

LCD控制器層---------->芯片原廠完成(這里是三星提供, s3c-fb.c)

LCD屏物理參數(shù)-------->我們自己添加(tiny4412-lcds.c)

我們需要做的就是只有一點(diǎn),就是添加平臺(tái)總線數(shù)據(jù),因?yàn)檫@個(gè)芯片的LCD控制器只有一個(gè),而LCD屏則可以使多選的,比如我們可以使用5寸的也可以使用7寸的,這兩者肯定是有一定的差異的,有差異,就可以采取數(shù)據(jù)總線的形式,這里就是,LCD驅(qū)動(dòng)采用了數(shù)據(jù)總線,我們根據(jù)自己的屏幕的實(shí)際物理特性,把相關(guān)參數(shù)添加到已經(jīng)構(gòu)件的總臺(tái)總線的驅(qū)動(dòng)數(shù)據(jù)層即可.

tiny4412的平臺(tái)自定義數(shù)據(jù)在mach-tiny4412.c里面,我們直接搜fb即可,然后會(huì)在smdk4x12_machine_init()里面發(fā)現(xiàn)如下代碼:

#ifdef CONFIG_TOUCHSCREEN_FT5X0Xstruct s3cfb_lcd *lcd = tiny4412_get_lcd();ft5x0x_pdata.screen_max_x = lcd->width;ft5x0x_pdata.screen_max_y = lcd->height; #endif

其中的tiny4412_get_lcd()就是獲取屏幕的參數(shù)的,這個(gè)函數(shù)定義在tiny4412-lcds.c,可以從這個(gè)c文件中發(fā)現(xiàn)X710屏幕的參數(shù)信息:

static struct s3cfb_lcd wsvga_x710 = {.width = 1024, // 水平像素.height = 600, // 垂直像素.p_width = 154, // 物理水平尺寸.p_height = 90, // 物理垂直尺寸,16:9.bpp = 24, // 像素位寬,RGB888.freq = 61, // 幀率.timing = {.h_fp = 84, // 水平前肩,水平無效時(shí)間.h_bp = 84, // 水平折返時(shí)間.h_sw = 88, // 水平穩(wěn)定時(shí)間.v_fp = 10, // 垂直無效時(shí)間.v_fpe = 1, // 均勻場垂直前肩.v_bp = 10, // 垂直折返時(shí)間.v_bpe = 1, // 均勻場垂直后肩.v_sw = 20, // 垂直穩(wěn)定時(shí)間},.polarity = {.rise_vclk = 1, // 上升沿?cái)?shù)據(jù)有效.inv_hsync = 1, // 水平極性反轉(zhuǎn).inv_vsync = 1, // 垂直極性反轉(zhuǎn).inv_vden = 0, // 數(shù)據(jù)使能}, };

實(shí)際開發(fā)中,我們的工作量就是上面這么多,就這幾個(gè)參數(shù),別的都是要么Linux內(nèi)核提供,要么芯片原廠提供,大大減小LCD驅(qū)動(dòng)開發(fā)的難度,上面這些參數(shù),一般響應(yīng)屏幕的datasheet上會(huì)寫出來,但這是個(gè)X710的,我沒找到,不過,還是可以透過這個(gè)配置信息知道這個(gè)屏幕的屬性,首先屏幕的分辨率是1024*600,物理尺寸是154x90(mm)接近于16:9,像素位寬是24,也就是RGB三原色各占8位,幀率從上面的數(shù)據(jù)可以大概算一下,接近于61Hz,一般也都要求至少60Hz才對(duì)人眼不會(huì)造成眩暈.然后timing結(jié)構(gòu)體里的數(shù)據(jù)都是要datasheet上提供的,但是下載不到這個(gè)datasheet,這里沒什么好說的,我都有寫注釋,自己可以百度到詳細(xì)答案.polarity結(jié)構(gòu)體里的東西也給了注釋,就不多說了.填完這個(gè)之后,我們來驗(yàn)證一下效果,需要做以下事情:

1).讓內(nèi)核加載適合我們屏幕的參數(shù):

這一步很坑,因?yàn)槲腋緵]找到在哪里去做配置,這個(gè)內(nèi)核沒有使用設(shè)備樹,然后也沒有向全志一樣使用script.bin的形式配置,make menuconfig也沒找到在哪里配置,然后看到頂層目錄的.config文件里有一個(gè)uboot傳參的句式:

CONFIG_CMDLINE="root=/dev/mmcblk0p2 rootfstype=ext4 init=/linuxrc console=ttySAC0,115200 lcd=HD700 ctp=2 skipcali=y"

然后知道應(yīng)該是可以通過uboot進(jìn)行選擇,但是,我這里直接了當(dāng),因?yàn)榕渲枚际峭ㄟ^tiny4412_get_lcd()這個(gè)函數(shù)來獲取的,所以,我直接修改這個(gè)函數(shù)即可,做如下修改:

struct s3cfb_lcd *tiny4412_get_lcd(void) { +++ lcd_idx = 7;return tiny4412_lcd_config[lcd_idx].lcd; }

其中新增加一句lcd_idx = 7;是因?yàn)閄710是可選配置結(jié)構(gòu)體里的第7個(gè),這樣.我們就選中了我們的屏幕X710.

2),配置了LCD之后,我們來驗(yàn)證一下LCD是否有效可用

最簡單的方式就是打開啟動(dòng)logo,如果開機(jī)有啟動(dòng)logo,就說明配置成功,如下,首先make menuconfig:

畫紅框的都要選中,然后,重新編譯內(nèi)核,把新生成的zImage下載到內(nèi)存卡,從內(nèi)存卡啟動(dòng),然后,如果順利的話,就可以看到4個(gè)小企鵝了,一個(gè)核心對(duì)應(yīng)一個(gè)小企鵝,4個(gè)代表有4顆核心,8個(gè)則代表8核心,以此類推.

下面這張圖是填參數(shù)的依據(jù),因?yàn)檫@里沒有找到X710的datasheet,所以,這里就不講這些了,不過如上都是有注明什么對(duì)應(yīng)什么的,一般看一下,就可以明白了.(從左到右,從上到下依次是SOC的LCD控制器時(shí)序圖,LCD屏幕datasheet時(shí)序圖,Linux內(nèi)核定義的時(shí)序圖,就這3個(gè),第4個(gè)和第2個(gè)是一個(gè),別看錯(cuò)了,它們雖然名字不同,但是卻是描述的同一個(gè)物理特性)

除了這些之外,我們還來做一個(gè)自己定制化的啟動(dòng)logo,玩一下,需要如下步驟:

1, 網(wǎng)上隨便下載一張圖片,比如jpg格式的,然后,我們通過GIMP軟件把圖片 像素修改成1028x600大小,位色改為224色,然后保存為ppm格式的ASCii文件2, 把ppm圖片放在如下路徑drivers/video/logo/logo_meizi_clut224.ppm3, drivers/video/logo/Makefileobj-$(CONFIG_LOGO_MEIZI_CLUT224) += logo_meizi_clut224.o4, drivers/video/logo/Kconfigconfig LOGO_MEIZI_CLUT224bool "Meizi Logo is very beautiful"help meizi meizi ,sexy lady5,將logo文件編譯進(jìn)內(nèi)核:Device Drivers --->Graphics support --->[*] Bootup logo ---> //提供啟動(dòng)的圖片[*] Standard black and white Linux logo (NEW)[*] Standard 16-color Linux logo (NEW)[*] Standard 224-color Linux logo (NEW) //默認(rèn)小企鵝圖片[*] Meizi Logo is very beautiful6, 指定使用哪個(gè)logo:include/linux/linux_logo.hextern const struct linux_logo logo_meizi_clut224;drivers/video/logo/logo.c|#ifdef CONFIG_LOGO_MEIZI_CLUT224logo = &logo_meizi_clut224;#endif 7, 編譯內(nèi)核:make zImage

下載zImage到內(nèi)存卡,然后啟動(dòng)就OK了.(因?yàn)閡boot沒有移植好,沒法通過tftp下載,燒進(jìn)mmc又麻煩,有機(jī)會(huì)再搞一下uboot).

因?yàn)橹邦I(lǐng)導(dǎo)說讓我"兼職"做LCD這一塊,所以,就準(zhǔn)備深入學(xué)習(xí)這一塊,但是目前實(shí)際上,并沒有拿到驅(qū)動(dòng)部門的任何資源,處境很尷尬,也始終得不到崗位調(diào)度,所以現(xiàn)在還是靠自己了,本來計(jì)劃自己實(shí)現(xiàn)一個(gè)LCD驅(qū)動(dòng).目前情況來看,還有更迫切的事,所以先貼一個(gè)三星官方的,有空再替換成自己寫的.

#include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/clk.h> #include <linux/fb.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/interrupt.h> #include <linux/pm_runtime.h>#include <mach/map.h> #include <mach/sysmmu.h> #include <mach/s3c-fb.h> #include <plat/regs-fb-v4.h> #include <plat/fb.h>#ifdef CONFIG_ION_EXYNOS #define CONFIG_FB_ION_EXYNOS #endif#if defined(CONFIG_FB_ION_EXYNOS) #include <linux/dma-buf.h> #include <plat/iovmm.h> #include <linux/sw_sync.h> #include <plat/devs.h> #include <linux/ion.h> #include <linux/kthread.h> #endif/* This driver will export a number of framebuffer interfaces depending* on the configuration passed in via the platform data. Each fb instance* maps to a hardware window. Currently there is no support for runtime* setting of the alpha-blending functions that each window has, so only* window 0 is actually useful.** Window 0 is treated specially, it is used for the basis of the LCD* output timings and as the control for the output power-down state. *//* note, the previous use of <mach/regs-fb.h> to get platform specific data* has been replaced by using the platform device name to pick the correct* configuration data for the system. */#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE #undef writel #define writel(v, r) do { \pr_debug("%s: %08x => %p\n", __func__, (unsigned int)v, r); \__raw_writel(v, r); \ } while (0) #endif /* FB_S3C_DEBUG_REGWRITE *//* irq_flags bits */ #define S3C_FB_VSYNC_IRQ_EN 0#define VSYNC_TIMEOUT_MSEC 60#undef CHECK_BANDWIDTH #define MAX_BW_PER_WINDOW (800 * 1280 * 4 * 60)/* disable blank */ #define ENABLE_FB_BLANK 0struct s3c_fb; extern struct ion_device *exynos_ion_dev;#define VALID_BPP(x) (1 << ((x) - 1))#define OSD_BASE(win, variant) ((variant).osd + ((win) * (variant).osd_stride)) #define VIDOSD_A(win, variant) (OSD_BASE(win, variant) + 0x00) #define VIDOSD_B(win, variant) (OSD_BASE(win, variant) + 0x04) #define VIDOSD_C(win, variant) (OSD_BASE(win, variant) + 0x08) #define VIDOSD_D(win, variant) (OSD_BASE(win, variant) + 0x0C)/*** struct s3c_fb_variant - fb variant information* @is_2443: Set if S3C2443/S3C2416 style hardware.* @nr_windows: The number of windows.* @vidtcon: The base for the VIDTCONx registers* @wincon: The base for the WINxCON registers.* @winmap: The base for the WINxMAP registers.* @keycon: The abse for the WxKEYCON registers.* @buf_start: Offset of buffer start registers.* @buf_size: Offset of buffer size registers.* @buf_end: Offset of buffer end registers.* @osd: The base for the OSD registers.* @palette: Address of palette memory, or 0 if none.* @has_prtcon: Set if has PRTCON register.* @has_shadowcon: Set if has SHADOWCON register.* @has_blendcon: Set if has BLENDCON register.* @has_clksel: Set if VIDCON0 register has CLKSEL bit.* @has_fixvclk: Set if VIDCON1 register has FIXVCLK bits.*/ struct s3c_fb_variant {unsigned int is_2443:1;unsigned short nr_windows;unsigned int vidtcon;unsigned short wincon;unsigned short winmap;unsigned short keycon;unsigned short buf_start;unsigned short buf_end;unsigned short buf_size;unsigned short osd;unsigned short osd_stride;unsigned short palette[S3C_FB_MAX_WIN];unsigned int has_prtcon:1;unsigned int has_shadowcon:1;unsigned int has_blendcon:1;unsigned int has_clksel:1;unsigned int has_fixvclk:1; };/*** struct s3c_fb_win_variant* @has_osd_c: Set if has OSD C register.* @has_osd_d: Set if has OSD D register.* @has_osd_alpha: Set if can change alpha transparency for a window.* @palette_sz: Size of palette in entries.* @palette_16bpp: Set if palette is 16bits wide.* @osd_size_off: If != 0, supports setting up OSD for a window; the appropriate* register is located at the given offset from OSD_BASE.* @valid_bpp: 1 bit per BPP setting to show valid bits-per-pixel.** valid_bpp bit x is set if (x+1)BPP is supported.*/ struct s3c_fb_win_variant {unsigned int has_osd_c:1;unsigned int has_osd_d:1;unsigned int has_osd_alpha:1;unsigned int palette_16bpp:1;unsigned short osd_size_off;unsigned short palette_sz;u32 valid_bpp; };/*** struct s3c_fb_driverdata - per-device type driver data for init time.* @variant: The variant information for this driver.* @win: The window information for each window.*/ struct s3c_fb_driverdata {struct s3c_fb_variant variant;struct s3c_fb_win_variant *win[S3C_FB_MAX_WIN]; };/*** struct s3c_fb_palette - palette information* @r: Red bitfield.* @g: Green bitfield.* @b: Blue bitfield.* @a: Alpha bitfield.*/ struct s3c_fb_palette {struct fb_bitfield r;struct fb_bitfield g;struct fb_bitfield b;struct fb_bitfield a; };#if defined(CONFIG_FB_ION_EXYNOS) struct s3c_dma_buf_data {struct ion_handle *ion_handle;struct dma_buf *dma_buf;struct dma_buf_attachment *attachment;struct sg_table *sg_table;dma_addr_t dma_addr;struct sync_fence *fence; };struct s3c_reg_data {struct list_head list;u32 shadowcon;u32 wincon[S3C_FB_MAX_WIN];u32 win_rgborder[S3C_FB_MAX_WIN];u32 winmap[S3C_FB_MAX_WIN];u32 vidosd_a[S3C_FB_MAX_WIN];u32 vidosd_b[S3C_FB_MAX_WIN];u32 vidosd_c[S3C_FB_MAX_WIN];u32 vidosd_d[S3C_FB_MAX_WIN];u32 vidw_alpha0[S3C_FB_MAX_WIN];u32 vidw_alpha1[S3C_FB_MAX_WIN];u32 blendeq[S3C_FB_MAX_WIN - 1];u32 vidw_buf_start[S3C_FB_MAX_WIN];u32 vidw_buf_end[S3C_FB_MAX_WIN];u32 vidw_buf_size[S3C_FB_MAX_WIN];struct s3c_dma_buf_data dma_buf_data[S3C_FB_MAX_WIN]; }; #endif/*** struct s3c_fb_win - per window private data for each framebuffer.* @windata: The platform data supplied for the window configuration.* @parent: The hardware that this window is part of.* @fbinfo: Pointer pack to the framebuffer info for this window.* @varint: The variant information for this window.* @palette_buffer: Buffer/cache to hold palette entries.* @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/* @index: The window number of this window.* @palette: The bitfields for changing r/g/b into a hardware palette entry.*/ struct s3c_fb_win {struct s3c_fb_pd_win *windata;struct s3c_fb *parent;struct fb_info *fbinfo;struct s3c_fb_palette palette;struct s3c_fb_win_variant variant;u32 *palette_buffer;u32 pseudo_palette[16];unsigned int index;#if defined(CONFIG_FB_ION_EXYNOS)struct s3c_dma_buf_data dma_buf_data;struct fb_var_screeninfo prev_var;struct fb_fix_screeninfo prev_fix;int fps; #endif };/*** struct s3c_fb_vsync - vsync information* @wait: a queue for processes waiting for vsync* @count: vsync interrupt count*/ struct s3c_fb_vsync {wait_queue_head_t wait;#if defined(CONFIG_FB_ION_EXYNOS) ktime_t timestamp;bool active;int irq_refcount;struct mutex irq_lock;struct task_struct *thread; #endifunsigned int count; };/*** struct s3c_fb_user_window - User window information* @x: X position of user window.* @y: Y position of user window.*/ struct s3c_fb_user_window {int x;int y; };/*** struct s3c_fb_user_chroma - User chroma key information* @enabled: Enabled/Disabled chroma key.* @red: red color key value for transparent pixel effect.* @green: green color key value for transparent pixel effect.* @blue: blue color key value for transparent pixel effect.*/ struct s3c_fb_user_chroma {int enabled;unsigned char red;unsigned char green;unsigned char blue; };/*** struct s3c_fb - overall hardware state of the hardware* @slock: The spinlock protection for this data sturcture.* @dev: The device that we bound to, for printing, etc.* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.* @lcd_clk: The clk (sclk) feeding pixclk.* @regs: The mapped hardware registers.* @variant: Variant information for this hardware.* @enabled: A bitmask of enabled hardware windows.* @output_on: Flag if the physical output is enabled.* @pdata: The platform configuration data passed with the device.* @windows: The hardware windows that have been claimed.* @irq_no: IRQ line number* @irq_flags: irq flags* @vsync_info: VSYNC-related information (count, queues...)*/ struct s3c_fb {spinlock_t slock;struct device *dev;struct clk *bus_clk;struct clk *lcd_clk;void __iomem *regs;struct s3c_fb_variant variant;unsigned char enabled;bool output_on;struct s3c_fb_platdata *pdata;struct s3c_fb_win *windows[S3C_FB_MAX_WIN];int irq_no;unsigned long irq_flags;struct s3c_fb_vsync vsync_info;#if defined(CONFIG_FB_ION_EXYNOS) struct mutex output_lock;struct ion_client *fb_ion_client;struct list_head update_regs_list;struct mutex update_regs_list_lock;struct kthread_worker update_regs_worker;struct task_struct *update_regs_thread;struct kthread_work update_regs_work;struct sw_sync_timeline *timeline;int timeline_max; #endif };#if defined(CONFIG_FB_ION_EXYNOS) static bool s3c_fb_validate_x_alignment(struct s3c_fb *sfb, int x, u32 w,u32 bits_per_pixel) {uint8_t pixel_alignment = 32 / bits_per_pixel;if (x % pixel_alignment) {dev_err(sfb->dev, "left X coordinate not properly aligned to ""%u-pixel boundary (bpp = %u, x = %u)\n",pixel_alignment, bits_per_pixel, x);return 0;}if ((x + w) % pixel_alignment) {dev_err(sfb->dev, "right X coordinate not properly aligned to ""%u-pixel boundary (bpp = %u, x = %u, w = %u)\n",pixel_alignment, bits_per_pixel, x, w);return 0;}return 1; } #endif/*** s3c_fb_validate_win_bpp - validate the bits-per-pixel for this mode.* @win: The device window.* @bpp: The bit depth.*/ static bool s3c_fb_validate_win_bpp(struct s3c_fb_win *win, unsigned int bpp) {return win->variant.valid_bpp & VALID_BPP(bpp); }/*** s3c_fb_check_var() - framebuffer layer request to verify a given mode.* @var: The screen information to verify.* @info: The framebuffer device.** Framebuffer layer call to verify the given information and allow us to* update various information depending on the hardware capabilities.*/ static int s3c_fb_check_var(struct fb_var_screeninfo *var,struct fb_info *info) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;dev_dbg(sfb->dev, "checking parameters\n");var->xres_virtual = max(var->xres_virtual, var->xres);var->yres_virtual = max(var->yres_virtual, var->yres);if (!s3c_fb_validate_win_bpp(win, var->bits_per_pixel)) {dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",win->index, var->bits_per_pixel);return -EINVAL;}/* always ensure these are zero, for drop through cases below */var->transp.offset = 0;var->transp.length = 0;switch (var->bits_per_pixel) {case 1:case 2:case 4:case 8:if (sfb->variant.palette[win->index] != 0) {/* non palletised, A:1,R:2,G:3,B:2 mode */var->red.offset = 4;var->green.offset = 2;var->blue.offset = 0;var->red.length = 5;var->green.length = 3;var->blue.length = 2;var->transp.offset = 7;var->transp.length = 1;} else {var->red.offset = 0;var->red.length = var->bits_per_pixel;var->green = var->red;var->blue = var->red;}break;case 19:/* 666 with one bit alpha/transparency */var->transp.offset = 18;var->transp.length = 1;case 18:var->bits_per_pixel = 32;/* 666 format */var->red.offset = 12;var->green.offset = 6;var->blue.offset = 0;var->red.length = 6;var->green.length = 6;var->blue.length = 6; #ifdef CONFIG_FB_S3C_ORDER_BGRswap(var->red.offset, var->blue.offset); #endifbreak;case 16:/* 16 bpp, 565 format */var->red.offset = 11;var->green.offset = 5;var->blue.offset = 0;var->red.length = 5;var->green.length = 6;var->blue.length = 5; #ifdef CONFIG_FB_S3C_ORDER_BGRswap(var->red.offset, var->blue.offset); #endifbreak;case 32:case 28:case 25:var->transp.length = var->bits_per_pixel - 24;var->transp.offset = 24;/* drop through */case 24:/* our 24bpp is unpacked, so 32bpp */var->bits_per_pixel = 32;var->red.offset = 16;var->red.length = 8;var->green.offset = 8;var->green.length = 8;var->blue.offset = 0;var->blue.length = 8; #ifdef CONFIG_FB_S3C_ORDER_BGRswap(var->red.offset, var->blue.offset); #endifbreak;default:dev_err(sfb->dev, "invalid bpp\n");}#if defined(CONFIG_FB_ION_EXYNOS)win->fps = 60; #endifdev_dbg(sfb->dev, "%s: verified parameters\n", __func__);return 0; }/*** s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock.* @sfb: The hardware state.* @pixclock: The pixel clock wanted, in picoseconds.** Given the specified pixel clock, work out the necessary divider to get* close to the output frequency.*/ static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk) {unsigned long clk;unsigned long long tmp;unsigned int result;if (sfb->variant.has_clksel)clk = clk_get_rate(sfb->bus_clk);elseclk = clk_get_rate(sfb->lcd_clk);tmp = (unsigned long long)clk;tmp *= pixclk;do_div(tmp, 1000000000UL);result = (unsigned int)tmp / 1000;dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n",pixclk, clk, result, result ? clk / result : clk);return result; }/*** s3c_fb_align_word() - align pixel count to word boundary* @bpp: The number of bits per pixel* @pix: The value to be aligned.** Align the given pixel count so that it will start on an 32bit word* boundary.*/ static int s3c_fb_align_word(unsigned int bpp, unsigned int pix) {int pix_per_word;if (bpp > 16)return pix;pix_per_word = (8 * 32) / bpp;return ALIGN(pix, pix_per_word); }/*** vidosd_set_size() - set OSD size for a window** @win: the window to set OSD size for* @size: OSD size register value*/ static void vidosd_set_size(struct s3c_fb_win *win, u32 size) {struct s3c_fb *sfb = win->parent;/* OSD can be set up if osd_size_off != 0 for this window */if (win->variant.osd_size_off)writel(size, sfb->regs + OSD_BASE(win->index, sfb->variant)+ win->variant.osd_size_off); }/*** vidosd_set_alpha() - set alpha transparency for a window** @win: the window to set OSD size for* @alpha: alpha register value*/ static void vidosd_set_alpha(struct s3c_fb_win *win, u32 alpha) {struct s3c_fb *sfb = win->parent;if (win->variant.has_osd_alpha)writel(alpha, sfb->regs + VIDOSD_C(win->index, sfb->variant)); }/*** shadow_protect_win() - disable updating values from shadow registers at vsync** @win: window to protect registers for* @protect: 1 to protect (disable updates)*/ static void shadow_protect_win(struct s3c_fb_win *win, bool protect) {struct s3c_fb *sfb = win->parent;u32 reg;if (protect) {if (sfb->variant.has_prtcon) {writel(PRTCON_PROTECT, sfb->regs + PRTCON);} else if (sfb->variant.has_shadowcon) {reg = readl(sfb->regs + SHADOWCON);writel(reg | SHADOWCON_WINx_PROTECT(win->index),sfb->regs + SHADOWCON);}} else {if (sfb->variant.has_prtcon) {writel(0, sfb->regs + PRTCON);} else if (sfb->variant.has_shadowcon) {reg = readl(sfb->regs + SHADOWCON);writel(reg & ~SHADOWCON_WINx_PROTECT(win->index),sfb->regs + SHADOWCON);}} }static inline u32 fb_visual(u32 bits_per_pixel, unsigned short palette_sz) {switch (bits_per_pixel) {case 32:case 24:case 16:case 12:return FB_VISUAL_TRUECOLOR;case 8:if (palette_sz >= 256)return FB_VISUAL_PSEUDOCOLOR;elsereturn FB_VISUAL_TRUECOLOR;case 1:return FB_VISUAL_MONO01;default:return FB_VISUAL_PSEUDOCOLOR;} }static inline u32 fb_linelength(u32 xres_virtual, u32 bits_per_pixel) {return (xres_virtual * bits_per_pixel) / 8; }static inline u16 fb_panstep(u32 res, u32 res_virtual) {return res_virtual > res ? 1 : 0; }static inline u32 vidw_buf_size(u32 xres, u32 line_length, u32 bits_per_pixel) {u32 pagewidth = (xres * bits_per_pixel) >> 3;return (VIDW_BUF_SIZE_OFFSET(line_length - pagewidth) |VIDW_BUF_SIZE_PAGEWIDTH(pagewidth) |VIDW_BUF_SIZE_OFFSET_E(line_length - pagewidth) |VIDW_BUF_SIZE_PAGEWIDTH_E(pagewidth)); }static inline u32 vidosd_a(int x, int y) {return (VIDOSDxA_TOPLEFT_X(x) |VIDOSDxA_TOPLEFT_Y(y) |VIDOSDxA_TOPLEFT_X_E(x) |VIDOSDxA_TOPLEFT_Y_E(y)); }static inline u32 vidosd_b(int x, int y, u32 xres, u32 yres) {return (VIDOSDxB_BOTRIGHT_X(x + xres - 1) |VIDOSDxB_BOTRIGHT_Y(y + yres - 1) |VIDOSDxB_BOTRIGHT_X_E(x + xres - 1) |VIDOSDxB_BOTRIGHT_Y_E(y + yres - 1)); }static inline u32 vidosd_c(u8 r0, u8 g0, u8 b0, u8 r1, u8 g1, u8 b1) {return (VIDOSDxC_ALPHA0_R_H(r0) |VIDOSDxC_ALPHA0_G_H(g0) |VIDOSDxC_ALPHA0_B_H(b0) |VIDOSDxC_ALPHA1_R_H(r1) |VIDOSDxC_ALPHA1_G_H(g1) |VIDOSDxC_ALPHA1_B_H(b1)); }static inline u32 vidw_alpha(bool has_osd_alpha, u8 r, u8 g, u8 b) {if (has_osd_alpha)return (VIDWxALPHAx_R_L(r) |VIDWxALPHAx_G_L(g) |VIDWxALPHAx_B_L(b));elsereturn (VIDWxALPHAx_R(r) |VIDWxALPHAx_G(g) |VIDWxALPHAx_B(b)); }static inline u32 wincon(u32 bits_per_pixel, u32 transp_length, u32 red_length) {u32 data = 0;switch (bits_per_pixel) {case 1:data |= WINCON0_BPPMODE_1BPP;data |= WINCONx_BITSWP;data |= WINCONx_BURSTLEN_4WORD;break;case 2:data |= WINCON0_BPPMODE_2BPP;data |= WINCONx_BITSWP;data |= WINCONx_BURSTLEN_8WORD;break;case 4:data |= WINCON0_BPPMODE_4BPP;data |= WINCONx_BITSWP;data |= WINCONx_BURSTLEN_8WORD;break;case 8:if (transp_length != 0)data |= WINCON1_BPPMODE_8BPP_1232;elsedata |= WINCON0_BPPMODE_8BPP_PALETTE;data |= WINCONx_BURSTLEN_8WORD;data |= WINCONx_BYTSWP;break;case 16:if (transp_length == 1)data |= WINCON1_BPPMODE_16BPP_A1555 | WINCON1_BLD_PIX;else if (transp_length == 4)data |= WINCON1_BPPMODE_16BPP_A4444 | WINCON1_BLD_PIX;elsedata |= WINCON0_BPPMODE_16BPP_565;data |= WINCONx_HAWSWP;data |= WINCONx_BURSTLEN_16WORD;break;case 24:case 32:if (red_length == 6) {if (transp_length != 0)data |= WINCON1_BPPMODE_19BPP_A1666;elsedata |= WINCON1_BPPMODE_18BPP_666;} else if (transp_length == 1) {data |= WINCON1_BPPMODE_25BPP_A1888 | WINCON1_BLD_PIX;} else if ((transp_length == 4) || (transp_length == 8)) {data |= WINCON1_BPPMODE_28BPP_A4888 | \WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;} else {data |= WINCON0_BPPMODE_24BPP_888;}data |= WINCONx_WSWP;data |= WINCONx_BURSTLEN_16WORD;break;}return data; }static inline u32 blendeq(enum s3c_fb_blending blending, u8 transp_length) {u8 a, b;if (transp_length == 1 && blending == S3C_FB_BLENDING_PREMULT)blending = S3C_FB_BLENDING_COVERAGE;switch (blending) {case S3C_FB_BLENDING_NONE:a = BLENDEQ_COEF_ONE;b = BLENDEQ_COEF_ZERO;break;case S3C_FB_BLENDING_PREMULT:a = BLENDEQ_COEF_ONE;b = BLENDEQ_COEF_ONE_MINUS_ALPHA_A;break;case S3C_FB_BLENDING_COVERAGE:a = BLENDEQ_COEF_ALPHA_A;b = BLENDEQ_COEF_ONE_MINUS_ALPHA_A;break;default:return 0;}return (BLENDEQ_A_FUNC(a) |BLENDEQ_B_FUNC(b) |BLENDEQ_P_FUNC(BLENDEQ_COEF_ZERO) |BLENDEQ_Q_FUNC(BLENDEQ_COEF_ZERO)); }static void s3c_fb_clear_win(struct s3c_fb *sfb, int win); static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb); static void s3c_fb_enable_irq(struct s3c_fb *sfb);/*** s3c_fb_enable() - Set the state of the main LCD output* @sfb: The main framebuffer state.* @enable: The state to set.*/ static void s3c_fb_enable(struct s3c_fb *sfb, int enable) {struct s3c_fb_platdata *pd = sfb->pdata;int win_no, ret = 0;u32 reg;u32 vidcon0;if (enable && !sfb->output_on) {pm_runtime_get_sync(sfb->dev);clk_enable(sfb->bus_clk);if (!sfb->variant.has_clksel)clk_enable(sfb->lcd_clk);/* setup gpio and output polarity controls */pd->setup_gpio();writel(pd->vidcon1, sfb->regs + VIDCON1);/* set video clock running at under-run */if (sfb->variant.has_fixvclk) {reg = readl(sfb->regs + VIDCON1);reg &= ~VIDCON1_VCLK_MASK;reg |= VIDCON1_VCLK_RUN;writel(reg, sfb->regs + VIDCON1);}/* zero all windows before we do anything */for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)s3c_fb_clear_win(sfb, win_no);s3c_fb_set_rgb_timing(sfb); #if defined(CONFIG_FB_ION_EXYNOS)iovmm_activate(&s5p_device_fimd0.dev); #endifret = platform_sysmmu_on(sfb->dev);if (ret < 0) {pr_err("FIMD SYSMMU ON FAILED \n"); #if defined(CONFIG_FB_ION_EXYNOS)iovmm_deactivate(&s5p_device_fimd0.dev); #endifpm_runtime_put_sync(sfb->dev);return;}}vidcon0 = readl(sfb->regs + VIDCON0);if (enable) {vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;} else {/* see the note in the framebuffer datasheet about* why you cannot take both of these bits down at the* same time. */if (vidcon0 & VIDCON0_ENVID) {vidcon0 |= VIDCON0_ENVID;vidcon0 &= ~VIDCON0_ENVID_F;}}writel(vidcon0, sfb->regs + VIDCON0);if (!enable && sfb->output_on) {if (!sfb->variant.has_clksel)clk_disable(sfb->lcd_clk);clk_disable(sfb->bus_clk); #if defined(CONFIG_FB_ION_EXYNOS)iovmm_deactivate(&s5p_device_fimd0.dev); #endifpm_runtime_put_sync(sfb->dev);ret = platform_sysmmu_off(sfb->dev);if (ret < 0)pr_err("FIMD SYSMMU OFF FAILED \n");}sfb->output_on = enable; }#if defined(CHECK_BANDWIDTH) static unsigned int s3c_fb_calc_bandwidth(u32 w, u32 h, u32 bits_per_pixel, int fps) {unsigned int bw = w * h;bw *= DIV_ROUND_UP(bits_per_pixel, 8);bw *= fps;return bw; } #endif/*** s3c_fb_set_par() - framebuffer request to set new framebuffer state.* @info: The framebuffer to change.** Framebuffer layer request to set a new mode for the specified framebuffer*/ static int s3c_fb_set_par(struct fb_info *info) {struct fb_var_screeninfo *var = &info->var;struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;void __iomem *regs = sfb->regs;void __iomem *buf = regs;int win_no = win->index;u32 alpha = 0;u32 data;dev_dbg(sfb->dev, "setting framebuffer parameters\n");shadow_protect_win(win, 1);info->fix.visual = fb_visual(var->bits_per_pixel,win->variant.palette_sz);info->fix.line_length = fb_linelength(var->xres_virtual,var->bits_per_pixel);info->fix.xpanstep = fb_panstep(var->xres, var->xres_virtual);info->fix.ypanstep = fb_panstep(var->yres, var->yres_virtual);/* disable the window whilst we update it */writel(0, regs + WINCON(win_no));if (!sfb->output_on)s3c_fb_enable(sfb, 1);/* write the buffer address *//* start and end registers stride is 8 */buf = regs + win_no * 8;writel(info->fix.smem_start, buf + sfb->variant.buf_start);data = info->fix.smem_start + info->fix.line_length * var->yres;writel(data, buf + sfb->variant.buf_end);data = vidw_buf_size(var->xres, info->fix.line_length,var->bits_per_pixel);writel(data, regs + sfb->variant.buf_size + (win_no * 4));/* write 'OSD' registers to control position of framebuffer */data = vidosd_a(0, 0);writel(data, regs + VIDOSD_A(win_no, sfb->variant));data = vidosd_b(0, 0, var->xres, var->yres);writel(data, regs + VIDOSD_B(win_no, sfb->variant));alpha = vidosd_c(0, 0, 0, 0xff, 0xff, 0xff);vidosd_set_alpha(win, alpha);data = var->xres * var->yres;vidosd_set_size(win, data);data = vidw_alpha(win->variant.has_osd_alpha, 0, 0, 0);writel(data, regs + VIDW_ALPHA0(win_no));data = vidw_alpha(win->variant.has_osd_alpha, 0xff, 0xff, 0xff);writel(data, regs + VIDW_ALPHA1(win_no));/* Enable DMA channel for this window */if (sfb->variant.has_shadowcon) {data = readl(sfb->regs + SHADOWCON);data |= SHADOWCON_CHx_ENABLE(win_no);writel(data, sfb->regs + SHADOWCON);}data = WINCONx_ENWIN;sfb->enabled |= (1 << win->index);/* note, since we have to round up the bits-per-pixel, we end up* relying on the bitfield information for r/g/b/a to work out* exactly which mode of operation is intended. */data |= wincon(var->bits_per_pixel, var->transp.length,var->red.length);/* Enable the colour keying for the window below this one */if (win_no > 0) {u32 keycon0_data = 0, keycon1_data = 0;void __iomem *keycon = regs + sfb->variant.keycon;keycon0_data = ~(WxKEYCON0_KEYBL_EN |WxKEYCON0_KEYEN_F |WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);keycon1_data = WxKEYCON1_COLVAL(0xffffff);keycon += (win_no - 1) * 8;writel(keycon0_data, keycon + WKEYCON0);writel(keycon1_data, keycon + WKEYCON1);}writel(data, regs + sfb->variant.wincon + (win_no * 4));writel(0x0, regs + sfb->variant.winmap + (win_no * 4));/* Set alpha value width */if (sfb->variant.has_blendcon) {data = readl(sfb->regs + BLENDCON);data &= ~BLENDCON_NEW_MASK;if (var->transp.length > 4)data |= BLENDCON_NEW_8BIT_ALPHA_VALUE;elsedata |= BLENDCON_NEW_4BIT_ALPHA_VALUE;writel(data, sfb->regs + BLENDCON);}shadow_protect_win(win, 0);return 0; }/*** s3c_fb_update_palette() - set or schedule a palette update.* @sfb: The hardware information.* @win: The window being updated.* @reg: The palette index being changed.* @value: The computed palette value.** Change the value of a palette register, either by directly writing to* the palette (this requires the palette RAM to be disconnected from the* hardware whilst this is in progress) or schedule the update for later.** At the moment, since we have no VSYNC interrupt support, we simply set* the palette entry directly.*/ static void s3c_fb_update_palette(struct s3c_fb *sfb,struct s3c_fb_win *win,unsigned int reg, u32 value) {void __iomem *palreg;u32 palcon;palreg = sfb->regs + sfb->variant.palette[win->index];dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",__func__, win->index, reg, palreg, value);win->palette_buffer[reg] = value;palcon = readl(sfb->regs + WPALCON);writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);if (win->variant.palette_16bpp)writew(value, palreg + (reg * 2));elsewritel(value, palreg + (reg * 4));writel(palcon, sfb->regs + WPALCON); }static inline unsigned int chan_to_field(unsigned int chan,struct fb_bitfield *bf) {chan &= 0xffff;chan >>= 16 - bf->length;return chan << bf->offset; }/*** s3c_fb_setcolreg() - framebuffer layer request to change palette.* @regno: The palette index to change.* @red: The red field for the palette data.* @green: The green field for the palette data.* @blue: The blue field for the palette data.* @trans: The transparency (alpha) field for the palette data.* @info: The framebuffer being changed.*/ static int s3c_fb_setcolreg(unsigned regno,unsigned red, unsigned green, unsigned blue,unsigned transp, struct fb_info *info) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;unsigned int val;dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n",__func__, win->index, regno, red, green, blue);switch (info->fix.visual) {case FB_VISUAL_TRUECOLOR:/* true-colour, use pseudo-palette */if (regno < 16) {u32 *pal = info->pseudo_palette;val = chan_to_field(red, &info->var.red);val |= chan_to_field(green, &info->var.green);val |= chan_to_field(blue, &info->var.blue);pal[regno] = val;}break;case FB_VISUAL_PSEUDOCOLOR:if (regno < win->variant.palette_sz) {val = chan_to_field(red, &win->palette.r);val |= chan_to_field(green, &win->palette.g);val |= chan_to_field(blue, &win->palette.b);s3c_fb_update_palette(sfb, win, regno, val);}break;default:return 1; /* unknown type */}return 0; }/*** s3c_fb_blank() - blank or unblank the given window* @blank_mode: The blank state from FB_BLANK_** @info: The framebuffer to blank.** Framebuffer layer request to change the power state.*/ static int s3c_fb_blank(int blank_mode, struct fb_info *info) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;unsigned int index = win->index;u32 output_on = sfb->output_on;dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);switch (blank_mode) { #if ENABLE_FB_BLANKcase FB_BLANK_POWERDOWN:sfb->enabled &= ~(1 << index);case FB_BLANK_NORMAL:/* disable the DMA and display 0x0 (black) */ #if defined(CONFIG_FB_ION_EXYNOS)flush_kthread_worker(&sfb->update_regs_worker); #endifs3c_fb_enable(sfb, 0);break;case FB_BLANK_UNBLANK:s3c_fb_enable(sfb, 1);sfb->enabled |= (1 << index);break; #endifcase FB_BLANK_VSYNC_SUSPEND:case FB_BLANK_HSYNC_SUSPEND:default:return 1;}return output_on == sfb->output_on; }/*** s3c_fb_pan_display() - Pan the display.** Note that the offsets can be written to the device at any time, as their* values are latched at each vsync automatically. This also means that only* the last call to this function will have any effect on next vsync, but* there is no need to sleep waiting for it to prevent tearing.** @var: The screen information to verify.* @info: The framebuffer device.*/ static int s3c_fb_pan_display(struct fb_var_screeninfo *var,struct fb_info *info) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;void __iomem *buf = sfb->regs + win->index * 8;unsigned int start_boff, end_boff;/* Offset in bytes to the start of the displayed area */start_boff = var->yoffset * info->fix.line_length;/* X offset depends on the current bpp */if (info->var.bits_per_pixel >= 8) {start_boff += var->xoffset * (info->var.bits_per_pixel >> 3);} else {switch (info->var.bits_per_pixel) {case 4:start_boff += var->xoffset >> 1;break;case 2:start_boff += var->xoffset >> 2;break;case 1:start_boff += var->xoffset >> 3;break;default:dev_err(sfb->dev, "invalid bpp\n");return -EINVAL;}}/* Offset in bytes to the end of the displayed area */end_boff = start_boff + info->var.yres * info->fix.line_length;/* Temporarily turn off per-vsync update from shadow registers until* both start and end addresses are updated to prevent corruption */shadow_protect_win(win, 1);writel(info->fix.smem_start + start_boff, buf + sfb->variant.buf_start);writel(info->fix.smem_start + end_boff, buf + sfb->variant.buf_end);shadow_protect_win(win, 0);return 0; }/*** s3c_fb_enable_irq() - enable framebuffer interrupts* @sfb: main hardware state*/ static void s3c_fb_enable_irq(struct s3c_fb *sfb) {void __iomem *regs = sfb->regs;u32 irq_ctrl_reg;if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {/* IRQ disabled, enable it */irq_ctrl_reg = readl(regs + VIDINTCON0);irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;irq_ctrl_reg |= VIDINTCON0_INT_FRAME;irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;writel(irq_ctrl_reg, regs + VIDINTCON0);} }/*** s3c_fb_disable_irq() - disable framebuffer interrupts* @sfb: main hardware state*/ static void s3c_fb_disable_irq(struct s3c_fb *sfb) {void __iomem *regs = sfb->regs;u32 irq_ctrl_reg;if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {/* IRQ enabled, disable it */irq_ctrl_reg = readl(regs + VIDINTCON0);irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;writel(irq_ctrl_reg, regs + VIDINTCON0);} }#if defined(CONFIG_FB_ION_EXYNOS) static void s3c_fb_activate_vsync(struct s3c_fb *sfb) {int prev_refcount;mutex_lock(&sfb->vsync_info.irq_lock);prev_refcount = sfb->vsync_info.irq_refcount++;if (!prev_refcount)s3c_fb_enable_irq(sfb);mutex_unlock(&sfb->vsync_info.irq_lock); }static void s3c_fb_deactivate_vsync(struct s3c_fb *sfb) {int new_refcount;mutex_lock(&sfb->vsync_info.irq_lock);new_refcount = --sfb->vsync_info.irq_refcount;WARN_ON(new_refcount < 0);if (!new_refcount)s3c_fb_disable_irq(sfb);mutex_unlock(&sfb->vsync_info.irq_lock); } #endifstatic irqreturn_t s3c_fb_irq(int irq, void *dev_id) {struct s3c_fb *sfb = dev_id;void __iomem *regs = sfb->regs;u32 irq_sts_reg;ktime_t timestamp = ktime_get();spin_lock(&sfb->slock);irq_sts_reg = readl(regs + VIDINTCON1);if (irq_sts_reg & VIDINTCON1_INT_FRAME) {/* VSYNC interrupt, accept it */writel(VIDINTCON1_INT_FRAME, regs + VIDINTCON1);#if defined(CONFIG_FB_ION_EXYNOS)sfb->vsync_info.timestamp = timestamp; #endifsfb->vsync_info.count++;wake_up_interruptible(&sfb->vsync_info.wait);}/* We only support waiting for VSYNC for now, so it's safe* to always disable irqs here.*/spin_unlock(&sfb->slock);return IRQ_HANDLED; }/*** s3c_fb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout* @sfb: main hardware state* @crtc: head index.*/ static int s3c_fb_wait_for_vsync(struct s3c_fb *sfb, u32 crtc) {unsigned long count;int ret;if (crtc != 0) {return -ENODEV;}count = sfb->vsync_info.count; #if defined(CONFIG_FB_ION_EXYNOS)s3c_fb_activate_vsync(sfb); #endifret = wait_event_interruptible_timeout(sfb->vsync_info.wait,count != sfb->vsync_info.count,msecs_to_jiffies(VSYNC_TIMEOUT_MSEC));#if defined(CONFIG_FB_ION_EXYNOS)s3c_fb_deactivate_vsync(sfb); #endifif (ret == 0) {dev_err(sfb->dev, "wait for vsync failed\n");return -ETIMEDOUT;}return 0; }/*** s3c_fb_set_window_position() - Set framebuffer window position.* @info: The framebuffer information.* @user_window: User window information.*/ int s3c_fb_set_window_position(struct fb_info *info,struct s3c_fb_user_window user_window) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;struct fb_var_screeninfo *var = &info->var;int win_no = win->index;void __iomem *regs = sfb->regs;u32 data;shadow_protect_win(win, 1);/* write 'OSD' registers to control position of framebuffer */data = VIDOSDxA_TOPLEFT_X(user_window.x) | \VIDOSDxA_TOPLEFT_Y(user_window.y) | \VIDOSDxA_TOPLEFT_X_E(user_window.x) | \VIDOSDxA_TOPLEFT_Y_E(user_window.y);writel(data, regs + VIDOSD_A(win_no, sfb->variant));data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,user_window.x + var->xres - 1)) |VIDOSDxB_BOTRIGHT_Y(user_window.y + var->yres - 1) |VIDOSDxB_BOTRIGHT_X_E(s3c_fb_align_word(var->bits_per_pixel,user_window.x + var->xres - 1)) |VIDOSDxB_BOTRIGHT_Y_E(user_window.y + var->yres - 1);writel(data, regs + VIDOSD_B(win_no, sfb->variant));shadow_protect_win(win, 0);return 0; }/*** s3c_fb_set_chroma_key() - Set chroma key.* @info: The framebuffer information.* @user_chroma: User chroma key information.*/ int s3c_fb_set_chroma_key(struct fb_info *info,struct s3c_fb_user_chroma user_chroma) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;int win_no = win->index;void __iomem *regs = sfb->regs;void __iomem *keycon = regs + sfb->variant.keycon;u32 data = 0;u32 chroma_value;chroma_value = (((user_chroma.red & 0xff) << 16) |((user_chroma.green & 0xff) << 8) |((user_chroma.blue & 0xff) << 0));shadow_protect_win(win, 1);if (user_chroma.enabled)data |= WxKEYCON0_KEYEN_F;keycon += (win_no-1) * 8;writel(data, keycon + WKEYCON0);data = (chroma_value & 0xffffff);writel(data, keycon + WKEYCON1);shadow_protect_win(win, 0);return 0; }#if defined(CONFIG_FB_ION_EXYNOS) static int s3c_fb_wait_for_vsync_thread(void *data) {struct s3c_fb *sfb = data;unsigned long count;int ret;while (!kthread_should_stop()) {count = sfb->vsync_info.count;ret = wait_event_interruptible(sfb->vsync_info.wait,(count != sfb->vsync_info.count) && sfb->vsync_info.active);if (!ret) {sysfs_notify(&sfb->dev->kobj, NULL, "vsync");}}return 0; } #endif/*------------------------------------------------------------------ */static ssize_t s3c_fb_vsync_show(struct device *dev,struct device_attribute *attr, char *buf) {struct s3c_fb *sfb = dev_get_drvdata(dev); #if defined(CONFIG_FB_ION_EXYNOS)return scnprintf(buf, PAGE_SIZE, "%llu\n",ktime_to_ns(sfb->vsync_info.timestamp)); #elsereturn scnprintf(buf, PAGE_SIZE, "%u\n", sfb->vsync_info.count); #endif }static DEVICE_ATTR(vsync, S_IRUGO, s3c_fb_vsync_show, NULL);#if defined(CONFIG_FB_ION_EXYNOS) int s3c_fb_set_vsync_int(struct fb_info *info,bool active) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;bool prev_active = sfb->vsync_info.active;sfb->vsync_info.active = active;smp_wmb();if (active && !prev_active)s3c_fb_activate_vsync(sfb);else if (!active && prev_active)s3c_fb_deactivate_vsync(sfb);return 0; }static unsigned int s3c_fb_map_ion_handle(struct s3c_fb *sfb,struct s3c_dma_buf_data *dma, struct ion_handle *ion_handle,struct dma_buf *buf) {dma->fence = NULL;dma->dma_buf = buf;dma->attachment = dma_buf_attach(dma->dma_buf, sfb->dev);if (IS_ERR_OR_NULL(dma->attachment)) {dev_err(sfb->dev, "dma_buf_attach() failed: %ld\n",PTR_ERR(dma->attachment));goto err_buf_map_attach;}dma->sg_table = dma_buf_map_attachment(dma->attachment,DMA_BIDIRECTIONAL);if (IS_ERR_OR_NULL(dma->sg_table)) {dev_err(sfb->dev, "dma_buf_map_attachment() failed: %ld\n",PTR_ERR(dma->sg_table));goto err_buf_map_attachment;}dma->dma_addr = iovmm_map(&s5p_device_fimd0.dev, dma->sg_table->sgl, 0,dma->dma_buf->size);if (!dma->dma_addr || IS_ERR_VALUE(dma->dma_addr)) {dev_err(sfb->dev, "iovmm_map() failed: %d\n", dma->dma_addr);goto err_iovmm_map;}dma->ion_handle = ion_handle;return dma->dma_buf->size;err_iovmm_map:dma_buf_unmap_attachment(dma->attachment, dma->sg_table,DMA_BIDIRECTIONAL); err_buf_map_attachment:dma_buf_detach(dma->dma_buf, dma->attachment); err_buf_map_attach:return 0; }static void s3c_fb_free_dma_buf(struct s3c_fb *sfb,struct s3c_dma_buf_data *dma) {if (!dma->dma_addr)return;if (dma->fence)sync_fence_put(dma->fence);iovmm_unmap(sfb->dev, dma->dma_addr);dma_buf_unmap_attachment(dma->attachment, dma->sg_table,DMA_BIDIRECTIONAL);dma_buf_detach(dma->dma_buf, dma->attachment);dma_buf_put(dma->dma_buf);ion_free(sfb->fb_ion_client, dma->ion_handle);memset(dma, 0, sizeof(struct s3c_dma_buf_data)); }static u32 s3c_fb_red_length(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_RGBX_8888:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 8;case S3C_FB_PIXEL_FORMAT_RGBA_5551:return 5;case S3C_FB_PIXEL_FORMAT_RGB_565:return 5;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_red_offset(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_RGBX_8888:case S3C_FB_PIXEL_FORMAT_RGBA_5551:return 0;case S3C_FB_PIXEL_FORMAT_RGB_565:return 11;case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 16;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_green_length(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_RGBX_8888:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 8;case S3C_FB_PIXEL_FORMAT_RGBA_5551:return 5;case S3C_FB_PIXEL_FORMAT_RGB_565:return 6;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_green_offset(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_RGBX_8888:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 8;case S3C_FB_PIXEL_FORMAT_RGBA_5551:case S3C_FB_PIXEL_FORMAT_RGB_565:return 5;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_blue_length(int format) {return s3c_fb_red_length(format); }static u32 s3c_fb_blue_offset(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_RGBX_8888:return 16;case S3C_FB_PIXEL_FORMAT_RGBA_5551:return 10;case S3C_FB_PIXEL_FORMAT_RGB_565:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 0;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_transp_length(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 8;case S3C_FB_PIXEL_FORMAT_RGBA_5551:return 1;case S3C_FB_PIXEL_FORMAT_RGBX_8888:case S3C_FB_PIXEL_FORMAT_RGB_565:return 0;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_transp_offset(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 24;case S3C_FB_PIXEL_FORMAT_RGBA_5551:return 15;case S3C_FB_PIXEL_FORMAT_RGBX_8888:return s3c_fb_blue_offset(format);case S3C_FB_PIXEL_FORMAT_RGB_565:return 0;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_padding(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBX_8888:return 8;case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_RGBA_5551:case S3C_FB_PIXEL_FORMAT_RGB_565:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return 0;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static u32 s3c_fb_rgborder(int format) {switch (format) {case S3C_FB_PIXEL_FORMAT_RGBX_8888:case S3C_FB_PIXEL_FORMAT_RGBA_8888:case S3C_FB_PIXEL_FORMAT_RGBA_5551:return WIN_RGB_ORDER_RGB;case S3C_FB_PIXEL_FORMAT_RGB_565:case S3C_FB_PIXEL_FORMAT_BGRA_8888:return WIN_RGB_ORDER_BGR;default:pr_warn("s3c-fb: unrecognized pixel format %u\n", format);return 0;} }static int s3c_fb_set_win_buffer(struct s3c_fb *sfb, struct s3c_fb_win *win,struct s3c_fb_win_config *win_config, struct s3c_reg_data *regs) {struct ion_handle *handle;struct fb_var_screeninfo prev_var = win->fbinfo->var;struct dma_buf *buf;struct s3c_dma_buf_data dma_buf_data;unsigned short win_no = win->index;int ret;size_t buf_size, window_size;u8 alpha0, alpha1;if (win_config->format >= S3C_FB_PIXEL_FORMAT_MAX) {dev_err(sfb->dev, "unknown pixel format %u\n",win_config->format);return -EINVAL;}if (win_config->blending >= S3C_FB_BLENDING_MAX) {dev_err(sfb->dev, "unknown blending %u\n",win_config->blending);return -EINVAL;}if (win_no == 0 && win_config->blending != S3C_FB_BLENDING_NONE) {dev_err(sfb->dev, "blending not allowed on window 0\n");return -EINVAL;}if (win_config->w == 0 || win_config->h == 0) {dev_err(sfb->dev, "window is size 0 (w = %u, h = %u)\n",win_config->w, win_config->h);return -EINVAL;}win->fbinfo->var.red.length = s3c_fb_red_length(win_config->format);win->fbinfo->var.red.offset = s3c_fb_red_offset(win_config->format);win->fbinfo->var.green.length = s3c_fb_green_length(win_config->format);win->fbinfo->var.green.offset = s3c_fb_green_offset(win_config->format);win->fbinfo->var.blue.length = s3c_fb_blue_length(win_config->format);win->fbinfo->var.blue.offset = s3c_fb_blue_offset(win_config->format);win->fbinfo->var.transp.length =s3c_fb_transp_length(win_config->format);win->fbinfo->var.transp.offset =s3c_fb_transp_offset(win_config->format);win->fbinfo->var.bits_per_pixel = win->fbinfo->var.red.length +win->fbinfo->var.green.length +win->fbinfo->var.blue.length +win->fbinfo->var.transp.length +s3c_fb_padding(win_config->format);regs->win_rgborder[win_no] = s3c_fb_rgborder(win_config->format);if (win_config->w * win->fbinfo->var.bits_per_pixel / 8 < 128) {dev_err(sfb->dev, "window must be at least 128 bytes wide (width = %u, bpp = %u)\n",win_config->w,win->fbinfo->var.bits_per_pixel);ret = -EINVAL;goto err_invalid;}if (win_config->stride <win_config->w * win->fbinfo->var.bits_per_pixel / 8) {dev_err(sfb->dev, "stride shorter than buffer width (stride = %u, width = %u, bpp = %u)\n",win_config->stride, win_config->w,win->fbinfo->var.bits_per_pixel);ret = -EINVAL;goto err_invalid;}if (!s3c_fb_validate_x_alignment(sfb, win_config->x, win_config->w,win->fbinfo->var.bits_per_pixel)) {ret = -EINVAL;goto err_invalid;}handle = ion_import_dma_buf(sfb->fb_ion_client, win_config->fd);if (IS_ERR(handle)) {dev_err(sfb->dev, "failed to import fd\n");ret = PTR_ERR(handle);goto err_invalid;}buf = dma_buf_get(win_config->fd);if (IS_ERR_OR_NULL(buf)) {dev_err(sfb->dev, "dma_buf_get() failed: %ld\n",PTR_ERR(buf));ret = PTR_ERR(buf);goto err_buf_get;}buf_size = s3c_fb_map_ion_handle(sfb, &dma_buf_data, handle,buf);if (!buf_size) {ret = -ENOMEM;goto err_map;}handle = NULL;buf = NULL;if (win_config->fence_fd >= 0) {dma_buf_data.fence = sync_fence_fdget(win_config->fence_fd);if (!dma_buf_data.fence) {dev_err(sfb->dev, "failed to import fence fd\n");ret = -EINVAL;goto err_offset;}}window_size = win_config->stride * (win_config->h);win->fbinfo->fix.smem_start = dma_buf_data.dma_addr+ win_config->offset;win->fbinfo->fix.smem_len = window_size;win->fbinfo->var.xres = win_config->w;win->fbinfo->var.xres_virtual = win_config->stride * 8 /win->fbinfo->var.bits_per_pixel;win->fbinfo->var.yres = win->fbinfo->var.yres_virtual = win_config->h;win->fbinfo->var.xoffset = win_config->offset % win_config->stride;win->fbinfo->var.yoffset = win_config->offset / win_config->stride;win->fbinfo->fix.visual = fb_visual(win->fbinfo->var.bits_per_pixel,win->variant.palette_sz);win->fbinfo->fix.line_length = win_config->stride;win->fbinfo->fix.xpanstep = fb_panstep(win_config->w,win->fbinfo->var.xres_virtual);win->fbinfo->fix.ypanstep = fb_panstep(win_config->h, win_config->h);regs->dma_buf_data[win_no] = dma_buf_data;regs->vidw_buf_start[win_no] = win->fbinfo->fix.smem_start;regs->vidw_buf_end[win_no] = regs->vidw_buf_start[win_no] +window_size;regs->vidw_buf_size[win_no] = vidw_buf_size(win_config->w,win->fbinfo->fix.line_length,win->fbinfo->var.bits_per_pixel);regs->vidosd_a[win_no] = vidosd_a(win_config->x, win_config->y);regs->vidosd_b[win_no] = vidosd_b(win_config->x, win_config->y,win_config->w, win_config->h);if (win->fbinfo->var.transp.length == 1 &&win_config->blending == S3C_FB_BLENDING_NONE) {alpha0 = 0xff;alpha1 = 0xff;} else {alpha0 = 0;alpha1 = 0xff;}if (win->variant.has_osd_alpha)regs->vidosd_c[win_no] = vidosd_c(alpha0, alpha0, alpha0,alpha1, alpha1, alpha1);regs->vidw_alpha0[win_no] = vidw_alpha(win->variant.has_osd_alpha,alpha0, alpha0, alpha0);regs->vidw_alpha1[win_no] = vidw_alpha(win->variant.has_osd_alpha,alpha1, alpha1, alpha1);if (win->variant.osd_size_off) {u32 size = win_config->w * win_config->h;if (win->variant.has_osd_alpha)regs->vidosd_d[win_no] = size;elseregs->vidosd_c[win_no] = size;}regs->shadowcon |= SHADOWCON_CHx_ENABLE(win_no);regs->wincon[win_no] = wincon(win->fbinfo->var.bits_per_pixel,win->fbinfo->var.transp.length,win->fbinfo->var.red.length);if (win_no)regs->blendeq[win_no - 1] = blendeq(win_config->blending,win->fbinfo->var.transp.length);return 0;err_offset:s3c_fb_free_dma_buf(sfb, &dma_buf_data); err_map:if (buf)dma_buf_put(buf); err_buf_get:if (handle)ion_free(sfb->fb_ion_client, handle); err_invalid:win->fbinfo->var = prev_var;return ret; }static int s3c_fb_set_win_config(struct s3c_fb *sfb,struct s3c_fb_win_config_data *win_data) {struct s3c_fb_win_config *win_config = win_data->config;int ret = 0;unsigned short i;struct s3c_reg_data *regs;struct sync_fence *fence;struct sync_pt *pt;int fd;unsigned int bw = 0;fd = get_unused_fd();if (fd < 0)return fd;mutex_lock(&sfb->output_lock);if (!sfb->output_on) {sfb->timeline_max++;pt = sw_sync_pt_create(sfb->timeline, sfb->timeline_max);fence = sync_fence_create("display", pt);sync_fence_install(fence, fd);win_data->fence = fd;sw_sync_timeline_inc(sfb->timeline, 1);goto err;}regs = kzalloc(sizeof(struct s3c_reg_data), GFP_KERNEL);if (!regs) {dev_err(sfb->dev, "could not allocate s3c_reg_data\n");ret = -ENOMEM;goto err;}for (i = 0; i < sfb->variant.nr_windows; i++) {sfb->windows[i]->prev_fix = sfb->windows[i]->fbinfo->fix;sfb->windows[i]->prev_var = sfb->windows[i]->fbinfo->var;}for (i = 0; i < sfb->variant.nr_windows && !ret; i++) {struct s3c_fb_win_config *config = &win_config[i];struct s3c_fb_win *win = sfb->windows[i];bool enabled = 0;u32 color_map = WINxMAP_MAP | WINxMAP_MAP_COLOUR(0);switch (config->state) {case S3C_FB_WIN_STATE_DISABLED:break;case S3C_FB_WIN_STATE_COLOR:enabled = 1;color_map |= WINxMAP_MAP_COLOUR(config->color);regs->vidosd_a[i] = vidosd_a(config->x, config->y);regs->vidosd_b[i] = vidosd_b(config->x, config->y,config->w, config->h);break;case S3C_FB_WIN_STATE_BUFFER:ret = s3c_fb_set_win_buffer(sfb, win, config, regs);if (!ret) {enabled = 1;color_map = 0;}break;default:dev_warn(sfb->dev, "unrecognized window state %u",config->state);ret = -EINVAL;break;}if (enabled)regs->wincon[i] |= WINCONx_ENWIN;elseregs->wincon[i] &= ~WINCONx_ENWIN;regs->winmap[i] = color_map;#if defined(CHECK_BANDWIDTH)if (enabled && config->state == S3C_FB_WIN_STATE_BUFFER) {bw += s3c_fb_calc_bandwidth(config->w, config->h,win->fbinfo->var.bits_per_pixel,win->fps);} #endif}#if defined(CHECK_BANDWIDTH)dev_dbg(sfb->dev, "Total BW = %d Mbits, Max BW per window = %d Mbits\n",bw / (1024 * 1024), MAX_BW_PER_WINDOW / (1024 * 1024)); #endifif (ret) {for (i = 0; i < sfb->variant.nr_windows; i++) {sfb->windows[i]->fbinfo->fix =sfb->windows[i]->prev_fix;sfb->windows[i]->fbinfo->var =sfb->windows[i]->prev_var;s3c_fb_free_dma_buf(sfb, &regs->dma_buf_data[i]);}put_unused_fd(fd);kfree(regs);} else {mutex_lock(&sfb->update_regs_list_lock);sfb->timeline_max++;pt = sw_sync_pt_create(sfb->timeline, sfb->timeline_max);fence = sync_fence_create("display", pt);sync_fence_install(fence, fd);win_data->fence = fd;list_add_tail(&regs->list, &sfb->update_regs_list);mutex_unlock(&sfb->update_regs_list_lock);queue_kthread_work(&sfb->update_regs_worker,&sfb->update_regs_work);}err:mutex_unlock(&sfb->output_lock);return ret; }static void __s3c_fb_update_regs(struct s3c_fb *sfb, struct s3c_reg_data *regs) {unsigned short i;for (i = 0; i < sfb->variant.nr_windows; i++)shadow_protect_win(sfb->windows[i], 1);for (i = 0; i < sfb->variant.nr_windows; i++) {writel(regs->wincon[i], sfb->regs + WINCON(i));writel(regs->win_rgborder[i], sfb->regs + WIN_RGB_ORDER(i));writel(regs->winmap[i], sfb->regs + WINxMAP(i));writel(regs->vidosd_a[i],sfb->regs + VIDOSD_A(i, sfb->variant));writel(regs->vidosd_b[i],sfb->regs + VIDOSD_B(i, sfb->variant));if (sfb->windows[i]->variant.has_osd_c)writel(regs->vidosd_c[i],sfb->regs + VIDOSD_C(i, sfb->variant));if (sfb->windows[i]->variant.has_osd_d)writel(regs->vidosd_d[i],sfb->regs + VIDOSD_D(i, sfb->variant));writel(regs->vidw_alpha0[i],sfb->regs + VIDW_ALPHA0(i));writel(regs->vidw_alpha1[i],sfb->regs + VIDW_ALPHA1(i));writel(regs->vidw_buf_start[i],sfb->regs + VIDW_BUF_START(i));writel(regs->vidw_buf_end[i],sfb->regs + VIDW_BUF_END(i));writel(regs->vidw_buf_size[i],sfb->regs + VIDW_BUF_SIZE(i));if (i)writel(regs->blendeq[i - 1], sfb->regs + BLENDEQ(i));sfb->windows[i]->dma_buf_data = regs->dma_buf_data[i];}if (sfb->variant.has_shadowcon)writel(regs->shadowcon, sfb->regs + SHADOWCON);for (i = 0; i < sfb->variant.nr_windows; i++)shadow_protect_win(sfb->windows[i], 0); }static void s3c_fd_fence_wait(struct s3c_fb *sfb, struct sync_fence *fence) {int err = sync_fence_wait(fence, 1000);if (err >= 0)return;if (err == -ETIME) {err = sync_fence_wait(fence, 10 * MSEC_PER_SEC);} }static void s3c_fb_update_regs(struct s3c_fb *sfb, struct s3c_reg_data *regs) {struct s3c_dma_buf_data old_dma_bufs[S3C_FB_MAX_WIN];bool wait_for_vsync;int count = 100;int i;for (i = 0; i < sfb->variant.nr_windows; i++) {old_dma_bufs[i] = sfb->windows[i]->dma_buf_data;if (regs->dma_buf_data[i].fence) {s3c_fd_fence_wait(sfb, regs->dma_buf_data[i].fence);}}do {__s3c_fb_update_regs(sfb, regs);s3c_fb_wait_for_vsync(sfb, 0);wait_for_vsync = false;for (i = 0; i < sfb->variant.nr_windows; i++) {u32 new_start = regs->vidw_buf_start[i];u32 shadow_start = readl(sfb->regs +SHD_VIDW_BUF_START(i));if (unlikely(new_start != shadow_start)) {wait_for_vsync = true;break;}}} while (wait_for_vsync && count--);if (wait_for_vsync) {pr_err("%s: failed to update registers\n", __func__);for (i = 0; i < sfb->variant.nr_windows; i++)pr_err("window %d new value %08x register value %08x\n",i, regs->vidw_buf_start[i],readl(sfb->regs + SHD_VIDW_BUF_START(i)));}sw_sync_timeline_inc(sfb->timeline, 1);for (i = 0; i < sfb->variant.nr_windows; i++)s3c_fb_free_dma_buf(sfb, &old_dma_bufs[i]); }static void s3c_fb_update_regs_handler(struct kthread_work *work) {struct s3c_fb *sfb =container_of(work, struct s3c_fb, update_regs_work);struct s3c_reg_data *data, *next;struct list_head saved_list;mutex_lock(&sfb->update_regs_list_lock);saved_list = sfb->update_regs_list;list_replace_init(&sfb->update_regs_list, &saved_list);mutex_unlock(&sfb->update_regs_list_lock);list_for_each_entry_safe(data, next, &saved_list, list) {s3c_fb_update_regs(sfb, data);list_del(&data->list);kfree(data);} }static int s3c_fb_get_user_ion_handle(struct s3c_fb *sfb,struct s3c_fb_win *win,struct s3c_fb_user_ion_client *user_ion_client) {/* Create fd for ion_buffer */user_ion_client->fd = ion_share_dma_buf_fd(sfb->fb_ion_client,win->dma_buf_data.ion_handle);if (user_ion_client->fd < 0) {pr_err("ion_share_fd failed\n");return user_ion_client->fd;}return 0; } #endifstatic int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg) {struct s3c_fb_win *win = info->par;struct s3c_fb *sfb = win->parent;int ret;u32 crtc;int offset;struct fb_var_screeninfo *var = &info->var;union {struct s3c_fb_user_window user_window;struct s3c_fb_user_chroma user_chroma; #if defined(CONFIG_FB_ION_EXYNOS)struct s3c_fb_user_ion_client user_ion_client;struct s3c_fb_win_config_data win_data;u32 vsync; #endif} p;switch (cmd) {case FBIO_WAITFORVSYNC:if (get_user(crtc, (u32 __user *)arg)) {ret = -EFAULT;break;}ret = s3c_fb_wait_for_vsync(sfb, crtc);break;case S3CFB_WIN_POSITION:if (copy_from_user(&p.user_window,(struct s3c_fb_user_window __user *)arg,sizeof(p.user_window))) {ret = -EFAULT;break;}if (p.user_window.x < 0)p.user_window.x = 0;if (p.user_window.y < 0)p.user_window.y = 0;ret = s3c_fb_set_window_position(info, p.user_window);break;case S3CFB_WIN_SET_CHROMA:if (copy_from_user(&p.user_chroma,(struct s3c_fb_user_chroma __user *)arg,sizeof(p.user_chroma))) {ret = -EFAULT;break;}ret = s3c_fb_set_chroma_key(info, p.user_chroma);break;#if defined(CONFIG_FB_ION_EXYNOS)case S3CFB_SET_VSYNC_INT:if (get_user(p.vsync, (int __user *)arg)) {ret = -EFAULT;break;}ret = s3c_fb_set_vsync_int(info, p.vsync);break;case S3CFB_WIN_CONFIG:if (copy_from_user(&p.win_data,(struct s3c_fb_win_config_data __user *)arg,sizeof(p.win_data))) {ret = -EFAULT;break;}ret = s3c_fb_set_win_config(sfb, &p.win_data);if (ret)break;if (copy_to_user((struct s3c_fb_win_config_data __user *)arg,&p.win_data,sizeof(p.user_ion_client))) {ret = -EFAULT;break;}break;case S3CFB_GET_ION_USER_HANDLE:if (copy_from_user(&p.user_ion_client,(struct s3c_fb_user_ion_client __user *)arg,sizeof(p.user_ion_client))) {ret = -EFAULT;break;}if (s3c_fb_get_user_ion_handle(sfb, win, &p.user_ion_client)) {ret = -EFAULT;break;}offset = var->xres_virtual * var->yoffset + var->xoffset;offset *= var->bits_per_pixel / 8;p.user_ion_client.offset = offset;dev_dbg(sfb->dev, "Buffer offset: 0x%x\n",p.user_ion_client.offset);if (copy_to_user((struct s3c_fb_user_ion_client __user *)arg,&p.user_ion_client,sizeof(p.user_ion_client))) {ret = -EFAULT;break;}ret = 0;break; #endifdefault:ret = -ENOTTY;}return ret; }#if defined(CONFIG_FB_ION_EXYNOS) static int s3c_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) {struct s3c_fb_win *win = info->par;vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);return dma_buf_mmap(win->dma_buf_data.dma_buf, vma, 0); } #endifstatic struct fb_ops s3c_fb_ops = {.owner = THIS_MODULE,.fb_check_var = s3c_fb_check_var,.fb_set_par = s3c_fb_set_par,.fb_blank = s3c_fb_blank,.fb_setcolreg = s3c_fb_setcolreg,.fb_fillrect = cfb_fillrect,.fb_copyarea = cfb_copyarea,.fb_imageblit = cfb_imageblit,.fb_pan_display = s3c_fb_pan_display,.fb_ioctl = s3c_fb_ioctl, #if defined(CONFIG_FB_ION_EXYNOS).fb_mmap = s3c_fb_mmap, #endif };/*** s3c_fb_missing_pixclock() - calculates pixel clock* @mode: The video mode to change.** Calculate the pixel clock when none has been given through platform data.*/ static void s3c_fb_missing_pixclock(struct fb_videomode *mode) {u64 pixclk = 1000000000000ULL;u32 div;div = mode->left_margin + mode->hsync_len + mode->right_margin +mode->xres;div *= mode->upper_margin + mode->vsync_len + mode->lower_margin +mode->yres;div *= mode->refresh ? : 60;do_div(pixclk, div);mode->pixclock = pixclk; }/*** s3c_fb_alloc_memory() - allocate display memory for framebuffer window* @sfb: The base resources for the hardware.* @win: The window to initialise memory for.** Allocate memory for the given framebuffer.*/ static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb,struct s3c_fb_win *win) {struct s3c_fb_pd_win *windata = win->windata;unsigned int real_size, virt_size, size;struct fb_info *fbi = win->fbinfo;dma_addr_t map_dma; #if defined(CONFIG_FB_ION_EXYNOS)struct ion_handle *handle;struct dma_buf *buf;int ret; #endifdev_dbg(sfb->dev, "allocating memory for display\n");real_size = windata->xres * windata->yres;virt_size = windata->virtual_x * windata->virtual_y;dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",real_size, windata->xres, windata->yres,virt_size, windata->virtual_x, windata->virtual_y);size = (real_size > virt_size) ? real_size : virt_size;size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;size /= 8;fbi->fix.smem_len = size;size = PAGE_ALIGN(size);dev_dbg(sfb->dev, "want %u bytes for window\n", size); #if defined(CONFIG_FB_ION_EXYNOS) handle = ion_alloc(sfb->fb_ion_client, (size_t)size, 0,ION_HEAP_SYSTEM_MASK/*ION_HEAP_EXYNOS_MASK*/, 0);if (IS_ERR(handle)) {dev_err(sfb->dev, "failed to ion_alloc\n");return -ENOMEM;}buf = ion_share_dma_buf(sfb->fb_ion_client, handle);if (IS_ERR_OR_NULL(buf)) {dev_err(sfb->dev, "ion_share_dma_buf() failed\n");goto err_share_dma_buf;}ret = s3c_fb_map_ion_handle(sfb, &win->dma_buf_data, handle, buf);if (!ret)goto err_map;map_dma = win->dma_buf_data.dma_addr;#if defined(CONFIG_FRAMEBUFFER_CONSOLE)fbi->screen_base = ion_map_kernel(sfb->fb_ion_client,win->dma_buf_data.ion_handle); #endif #elsefbi->screen_base = dma_alloc_writecombine(sfb->dev, size,&map_dma, GFP_KERNEL);if (!fbi->screen_base)return -ENOMEM;dev_dbg(sfb->dev, "mapped %x to %p\n",(unsigned int)map_dma, fbi->screen_base);memset(fbi->screen_base, 0x0, size); #endiffbi->fix.smem_start = map_dma;return 0;#if defined(CONFIG_FB_ION_EXYNOS) err_map:dma_buf_put(buf); err_share_dma_buf:ion_free(sfb->fb_ion_client, handle);return -ENOMEM; #endif }/*** s3c_fb_free_memory() - free the display memory for the given window* @sfb: The base resources for the hardware.* @win: The window to free the display memory for.** Free the display memory allocated by s3c_fb_alloc_memory().*/ static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win) { #if defined(CONFIG_FB_ION_EXYNOS)s3c_fb_free_dma_buf(sfb, &win->dma_buf_data); #elsestruct fb_info *fbi = win->fbinfo;if (fbi->screen_base)dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),fbi->screen_base, fbi->fix.smem_start); #endif }/*** s3c_fb_release_win() - release resources for a framebuffer window.* @win: The window to cleanup the resources for.** Release the resources that where claimed for the hardware window,* such as the framebuffer instance and any memory claimed for it.*/ static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win) {u32 data;if (win->fbinfo) {if (sfb->variant.has_shadowcon) {data = readl(sfb->regs + SHADOWCON);data &= ~SHADOWCON_CHx_ENABLE(win->index);data &= ~SHADOWCON_CHx_LOCAL_ENABLE(win->index);writel(data, sfb->regs + SHADOWCON);}unregister_framebuffer(win->fbinfo);if (win->fbinfo->cmap.len)fb_dealloc_cmap(&win->fbinfo->cmap);s3c_fb_free_memory(sfb, win);framebuffer_release(win->fbinfo);} }/*** s3c_fb_probe_win() - register an hardware window* @sfb: The base resources for the hardware* @variant: The variant information for this window.* @res: Pointer to where to place the resultant window.** Allocate and do the basic initialisation for one of the hardware's graphics* windows.*/ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,struct s3c_fb_win_variant *variant,struct s3c_fb_win **res) {struct fb_var_screeninfo *var;struct fb_videomode initmode;struct s3c_fb_pd_win *windata;struct s3c_fb_win *win;struct fb_info *fbinfo;int palette_size;int ret;dev_dbg(sfb->dev, "probing window %d, variant %p\n", win_no, variant);palette_size = variant->palette_sz * 4;fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +palette_size * sizeof(u32), sfb->dev);if (!fbinfo) {dev_err(sfb->dev, "failed to allocate framebuffer\n");return -ENOENT;}windata = sfb->pdata->win[win_no];initmode = *sfb->pdata->vtiming;WARN_ON(windata->max_bpp == 0);WARN_ON(windata->xres == 0);WARN_ON(windata->yres == 0);win = fbinfo->par;*res = win;var = &fbinfo->var;win->variant = *variant;win->fbinfo = fbinfo;win->parent = sfb;win->windata = windata;win->index = win_no;win->palette_buffer = (u32 *)(win + 1);ret = s3c_fb_alloc_memory(sfb, win);if (ret) {dev_err(sfb->dev, "failed to allocate display memory\n");return ret;}/* setup the r/b/g positions for the window's palette */if (win->variant.palette_16bpp) {/* Set RGB 5:6:5 as default */win->palette.r.offset = 11;win->palette.r.length = 5;win->palette.g.offset = 5;win->palette.g.length = 6;win->palette.b.offset = 0;win->palette.b.length = 5;} else {/* Set 8bpp or 8bpp and 1bit alpha */win->palette.r.offset = 16;win->palette.r.length = 8;win->palette.g.offset = 8;win->palette.g.length = 8;win->palette.b.offset = 0;win->palette.b.length = 8;}/* setup the initial video mode from the window */initmode.xres = windata->xres;initmode.yres = windata->yres;fb_videomode_to_var(&fbinfo->var, &initmode);fbinfo->var.width = windata->width;fbinfo->var.height = windata->height;fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;fbinfo->fix.accel = FB_ACCEL_NONE;fbinfo->var.activate = FB_ACTIVATE_NOW;fbinfo->var.vmode = FB_VMODE_NONINTERLACED;fbinfo->var.bits_per_pixel = windata->default_bpp;fbinfo->fbops = &s3c_fb_ops;fbinfo->flags = FBINFO_FLAG_DEFAULT;fbinfo->pseudo_palette = &win->pseudo_palette;/* prepare to actually start the framebuffer */ret = s3c_fb_check_var(&fbinfo->var, fbinfo);if (ret < 0) {dev_err(sfb->dev, "check_var failed on initial video params\n");return ret;}/* create initial colour map */ret = fb_alloc_cmap(&fbinfo->cmap, win->variant.palette_sz, 1);if (ret == 0)fb_set_cmap(&fbinfo->cmap, fbinfo);elsedev_err(sfb->dev, "failed to allocate fb cmap\n");s3c_fb_set_par(fbinfo);dev_dbg(sfb->dev, "about to register framebuffer\n");/* run the check_var and set_par on our configuration. */ret = register_framebuffer(fbinfo);if (ret < 0) {dev_err(sfb->dev, "failed to register framebuffer\n");return ret;}dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);return 0; }/*** s3c_fb_set_rgb_timing() - set video timing for rgb interface.* @sfb: The base resources for the hardware.** Set horizontal and vertical lcd rgb interface timing.*/ static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb) {struct fb_videomode *vmode = sfb->pdata->vtiming;void __iomem *regs = sfb->regs;int clkdiv;u32 data;if (!vmode->pixclock)s3c_fb_missing_pixclock(vmode);clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock);data = sfb->pdata->vidcon0;data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);if (clkdiv > 1)data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;elsedata &= ~VIDCON0_CLKDIR; /* 1:1 clock */if (sfb->variant.is_2443)data |= (1 << 5);data |= VIDCON0_ENVID | VIDCON0_ENVID_F;writel(data, regs + VIDCON0);#ifdef CONFIG_FB_S3C_ORDER_BGRdata = readl(sfb->regs + VIDCON2);data &= ~(VIDCON2_RGB_ORDER_E_MASK | VIDCON2_RGB_ORDER_O_MASK);data |= VIDCON2_RGB_ORDER_E_BGR | VIDCON2_RGB_ORDER_O_BGR;writel(data, sfb->regs + VIDCON2); #endifif (sfb->variant.has_blendcon) {data = readl(sfb->regs + BLENDCON);data &= ~BLENDCON_NEW_MASK;data |= BLENDCON_NEW_8BIT_ALPHA_VALUE;writel(data, sfb->regs + BLENDCON);}data = VIDTCON0_VBPD(vmode->upper_margin - 1) |VIDTCON0_VFPD(vmode->lower_margin - 1) |VIDTCON0_VSPW(vmode->vsync_len - 1);writel(data, regs + sfb->variant.vidtcon);data = VIDTCON1_HBPD(vmode->left_margin - 1) |VIDTCON1_HFPD(vmode->right_margin - 1) |VIDTCON1_HSPW(vmode->hsync_len - 1);writel(data, regs + sfb->variant.vidtcon + 4);data = VIDTCON2_LINEVAL(vmode->yres - 1) |VIDTCON2_HOZVAL(vmode->xres - 1) |VIDTCON2_LINEVAL_E(vmode->yres - 1) |VIDTCON2_HOZVAL_E(vmode->xres - 1);writel(data, regs + sfb->variant.vidtcon + 8); }/*** s3c_fb_clear_win() - clear hardware window registers.* @sfb: The base resources for the hardware.* @win: The window to process.** Reset the specific window registers to a known state.*/ static void s3c_fb_clear_win(struct s3c_fb *sfb, int win) {void __iomem *regs = sfb->regs;u32 reg;writel(0, regs + sfb->variant.wincon + (win * 4));writel(0, regs + VIDOSD_A(win, sfb->variant));writel(0, regs + VIDOSD_B(win, sfb->variant));writel(0, regs + VIDOSD_C(win, sfb->variant));if (sfb->variant.has_shadowcon) {reg = readl(sfb->regs + SHADOWCON);reg &= ~(SHADOWCON_WINx_PROTECT(win) |SHADOWCON_CHx_ENABLE(win) |SHADOWCON_CHx_LOCAL_ENABLE(win));writel(reg, sfb->regs + SHADOWCON);}reg = readl(sfb->regs + WINCON(win));reg &= ~WINCONx_ENWIN;writel(reg, sfb->regs + WINCON(win)); }static int __devinit s3c_fb_probe(struct platform_device *pdev) {const struct platform_device_id *platid;struct s3c_fb_driverdata *fbdrv;struct device *dev = &pdev->dev;struct s3c_fb_platdata *pd;struct s3c_fb *sfb;struct s3c_fb_win *fbwin;struct resource *res;int win;int ret = 0;u32 reg;platid = platform_get_device_id(pdev);fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;if (fbdrv->variant.nr_windows > S3C_FB_MAX_WIN) {dev_err(dev, "too many windows, cannot attach\n");return -EINVAL;}pd = pdev->dev.platform_data;if (!pd) {dev_err(dev, "no platform data specified\n");return -EINVAL;}sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL);if (!sfb) {dev_err(dev, "no memory for framebuffers\n");return -ENOMEM;}dev_dbg(dev, "allocate new framebuffer %p\n", sfb);sfb->dev = dev;sfb->pdata = pd;sfb->variant = fbdrv->variant;spin_lock_init(&sfb->slock); #if defined(CONFIG_FB_ION_EXYNOS)mutex_init(&sfb->output_lock);INIT_LIST_HEAD(&sfb->update_regs_list);mutex_init(&sfb->update_regs_list_lock);init_kthread_worker(&sfb->update_regs_worker);sfb->update_regs_thread = kthread_run(kthread_worker_fn,&sfb->update_regs_worker, "s3c-fb");if (IS_ERR(sfb->update_regs_thread)) {int err = PTR_ERR(sfb->update_regs_thread);sfb->update_regs_thread = NULL;dev_err(dev, "failed to run update_regs thread\n");return err;}init_kthread_work(&sfb->update_regs_work, s3c_fb_update_regs_handler);sfb->timeline = sw_sync_timeline_create("s3c-fb");sfb->timeline_max = 1;/* XXX need to cleanup on errors */ #endifsfb->bus_clk = clk_get(dev, "lcd");if (IS_ERR(sfb->bus_clk)) {dev_err(dev, "failed to get bus clock\n");ret = PTR_ERR(sfb->bus_clk);goto err_sfb;}clk_enable(sfb->bus_clk);if (!sfb->variant.has_clksel) {sfb->lcd_clk = clk_get(dev, "sclk_fimd");if (IS_ERR(sfb->lcd_clk)) {dev_err(dev, "failed to get lcd clock\n");ret = PTR_ERR(sfb->lcd_clk);goto err_bus_clk;}clk_enable(sfb->lcd_clk);}pm_runtime_enable(sfb->dev);res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {dev_err(dev, "failed to find registers\n");ret = -ENOENT;goto err_lcd_clk;}sfb->regs = devm_request_and_ioremap(dev, res);if (!sfb->regs) {dev_err(dev, "failed to map registers\n");ret = -ENXIO;goto err_lcd_clk;}res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (!res) {dev_err(dev, "failed to acquire irq resource\n");ret = -ENOENT;goto err_lcd_clk;}sfb->irq_no = res->start;ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq,0, "s3c_fb", sfb);if (ret) {dev_err(dev, "irq request failed\n");goto err_lcd_clk;}dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);platform_set_drvdata(pdev, sfb); #if defined(CONFIG_FB_ION_EXYNOS)mutex_init(&sfb->vsync_info.irq_lock);ret = device_create_file(sfb->dev, &dev_attr_vsync);if (ret) {dev_err(sfb->dev, "failed to create vsync file\n");} #endif/* setup gpio and output polarity controls */pd->setup_gpio();writel(pd->vidcon1, sfb->regs + VIDCON1);/* set video clock running at under-run */if (sfb->variant.has_fixvclk) {reg = readl(sfb->regs + VIDCON1);reg &= ~VIDCON1_VCLK_MASK;reg |= VIDCON1_VCLK_RUN;writel(reg, sfb->regs + VIDCON1);}/* zero all windows before we do anything */for (win = 0; win < fbdrv->variant.nr_windows; win++)s3c_fb_clear_win(sfb, win);/* initialise colour key controls */for (win = 0; win < (fbdrv->variant.nr_windows - 1); win++) {void __iomem *regs = sfb->regs + sfb->variant.keycon;regs += (win * 8);writel(0xffffff, regs + WKEYCON0);writel(0xffffff, regs + WKEYCON1);}#if defined(CONFIG_FB_ION_EXYNOS)sfb->fb_ion_client = ion_client_create(exynos_ion_dev,"fimd");if (IS_ERR(sfb->fb_ion_client)) {dev_err(sfb->dev, "failed to ion_client_create\n");goto err_pm_runtime;} #endifs3c_fb_set_rgb_timing(sfb);/* we have the register setup, start allocating framebuffers */init_waitqueue_head(&sfb->vsync_info.wait);for (win = 0; win < fbdrv->variant.nr_windows; win++) {if (!pd->win[win])continue;ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win],&sfb->windows[win]);if (ret < 0) {dev_err(dev, "failed to create window %d\n", win);for (; win >= 0; win--)s3c_fb_release_win(sfb, sfb->windows[win]);goto err_pm_runtime;}}#if defined(CONFIG_FB_ION_EXYNOS)sfb->vsync_info.thread = kthread_run(s3c_fb_wait_for_vsync_thread,sfb, "s3c-fb-vsync");if (sfb->vsync_info.thread == ERR_PTR(-ENOMEM)) {dev_err(sfb->dev, "failed to run vsync thread\n");sfb->vsync_info.thread = NULL;} #endif#if defined(CONFIG_LOGO) && !defined(CONFIG_FRAMEBUFFER_CONSOLE)/* Start display and show logo on boot */fbwin = sfb->windows[0];if (fb_prepare_logo(fbwin->fbinfo, FB_ROTATE_UR)) { #if defined(CONFIG_FB_ION_EXYNOS)fbwin->fbinfo->screen_base = ion_map_kernel(sfb->fb_ion_client,fbwin->dma_buf_data.ion_handle); #endifprintk("Start display and show logo\n");fb_set_cmap(&fbwin->fbinfo->cmap, fbwin->fbinfo);fb_show_logo(fbwin->fbinfo, FB_ROTATE_UR);#if defined(CONFIG_FB_ION_EXYNOS)ion_unmap_kernel(sfb->fb_ion_client, fbwin->dma_buf_data.ion_handle); #endif} #endifplatform_set_drvdata(pdev, sfb);return 0;err_pm_runtime:pm_runtime_put_sync(sfb->dev);err_lcd_clk:pm_runtime_disable(sfb->dev);if (!sfb->variant.has_clksel) {clk_disable(sfb->lcd_clk);clk_put(sfb->lcd_clk);}err_bus_clk:clk_disable(sfb->bus_clk);clk_put(sfb->bus_clk);err_sfb:return ret; }/*** s3c_fb_remove() - Cleanup on module finalisation* @pdev: The platform device we are bound to.** Shutdown and then release all the resources that the driver allocated* on initialisation.*/ static int __devexit s3c_fb_remove(struct platform_device *pdev) {struct s3c_fb *sfb = platform_get_drvdata(pdev);int win;pm_runtime_get_sync(sfb->dev);for (win = 0; win < S3C_FB_MAX_WIN; win++)if (sfb->windows[win])s3c_fb_release_win(sfb, sfb->windows[win]);if (!sfb->variant.has_clksel) {clk_disable(sfb->lcd_clk);clk_put(sfb->lcd_clk);}clk_disable(sfb->bus_clk);clk_put(sfb->bus_clk);pm_runtime_put_sync(sfb->dev);pm_runtime_disable(sfb->dev);return 0; }#ifdef CONFIG_PM_SLEEP static int s3c_fb_suspend(struct device *dev) {return 0; }static int s3c_fb_resume(struct device *dev) {return 0; } #endif#ifdef CONFIG_PM_RUNTIME static int s3c_fb_runtime_suspend(struct device *dev) {struct platform_device *pdev = to_platform_device(dev);struct s3c_fb *sfb = platform_get_drvdata(pdev);if (!sfb->variant.has_clksel)clk_disable(sfb->lcd_clk);clk_disable(sfb->bus_clk);return 0; }static int s3c_fb_runtime_resume(struct device *dev) {struct platform_device *pdev = to_platform_device(dev);struct s3c_fb *sfb = platform_get_drvdata(pdev);struct s3c_fb_platdata *pd = sfb->pdata;clk_enable(sfb->bus_clk);if (!sfb->variant.has_clksel)clk_enable(sfb->lcd_clk);/* setup gpio and output polarity controls */pd->setup_gpio();writel(pd->vidcon1, sfb->regs + VIDCON1);return 0; } #endif#define VALID_BPP124 (VALID_BPP(1) | VALID_BPP(2) | VALID_BPP(4)) #define VALID_BPP1248 (VALID_BPP124 | VALID_BPP(8))static struct s3c_fb_win_variant s3c_fb_data_64xx_wins[] = {[0] = {.has_osd_c = 1,.osd_size_off = 0x8,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(24)),},[1] = {.has_osd_c = 1,.has_osd_d = 1,.osd_size_off = 0xc,.has_osd_alpha = 1,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(28)),},[2] = {.has_osd_c = 1,.has_osd_d = 1,.osd_size_off = 0xc,.has_osd_alpha = 1,.palette_sz = 16,.palette_16bpp = 1,.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(28)),},[3] = {.has_osd_c = 1,.has_osd_alpha = 1,.palette_sz = 16,.palette_16bpp = 1,.valid_bpp = (VALID_BPP124 | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(28)),},[4] = {.has_osd_c = 1,.has_osd_alpha = 1,.palette_sz = 4,.palette_16bpp = 1,.valid_bpp = (VALID_BPP(1) | VALID_BPP(2) |VALID_BPP(16) | VALID_BPP(18) |VALID_BPP(19) | VALID_BPP(24) |VALID_BPP(25) | VALID_BPP(28)),}, };static struct s3c_fb_win_variant s3c_fb_data_s5p_wins[] = {[0] = {.has_osd_c = 1,.osd_size_off = 0x8,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |VALID_BPP(15) | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(32)),},[1] = {.has_osd_c = 1,.has_osd_d = 1,.osd_size_off = 0xc,.has_osd_alpha = 1,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |VALID_BPP(15) | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(32)),},[2] = {.has_osd_c = 1,.has_osd_d = 1,.osd_size_off = 0xc,.has_osd_alpha = 1,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |VALID_BPP(15) | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(32)),},[3] = {.has_osd_c = 1,.has_osd_alpha = 1,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |VALID_BPP(15) | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(32)),},[4] = {.has_osd_c = 1,.has_osd_alpha = 1,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(13) |VALID_BPP(15) | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(32)),}, };static struct s3c_fb_driverdata s3c_fb_data_64xx = {.variant = {.nr_windows = 5,.vidtcon = VIDTCON0,.wincon = WINCON(0),.winmap = WINxMAP(0),.keycon = WKEYCON,.osd = VIDOSD_BASE,.osd_stride = 16,.buf_start = VIDW_BUF_START(0),.buf_size = VIDW_BUF_SIZE(0),.buf_end = VIDW_BUF_END(0),.palette = {[0] = 0x400,[1] = 0x800,[2] = 0x300,[3] = 0x320,[4] = 0x340,},.has_prtcon = 1,.has_clksel = 1,},.win[0] = &s3c_fb_data_64xx_wins[0],.win[1] = &s3c_fb_data_64xx_wins[1],.win[2] = &s3c_fb_data_64xx_wins[2],.win[3] = &s3c_fb_data_64xx_wins[3],.win[4] = &s3c_fb_data_64xx_wins[4], };static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = {.variant = {.nr_windows = 5,.vidtcon = VIDTCON0,.wincon = WINCON(0),.winmap = WINxMAP(0),.keycon = WKEYCON,.osd = VIDOSD_BASE,.osd_stride = 16,.buf_start = VIDW_BUF_START(0),.buf_size = VIDW_BUF_SIZE(0),.buf_end = VIDW_BUF_END(0),.palette = {[0] = 0x2400,[1] = 0x2800,[2] = 0x2c00,[3] = 0x3000,[4] = 0x3400,},.has_prtcon = 1,.has_blendcon = 1,.has_clksel = 1,},.win[0] = &s3c_fb_data_s5p_wins[0],.win[1] = &s3c_fb_data_s5p_wins[1],.win[2] = &s3c_fb_data_s5p_wins[2],.win[3] = &s3c_fb_data_s5p_wins[3],.win[4] = &s3c_fb_data_s5p_wins[4], };static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {.variant = {.nr_windows = 5,.vidtcon = VIDTCON0,.wincon = WINCON(0),.winmap = WINxMAP(0),.keycon = WKEYCON,.osd = VIDOSD_BASE,.osd_stride = 16,.buf_start = VIDW_BUF_START(0),.buf_size = VIDW_BUF_SIZE(0),.buf_end = VIDW_BUF_END(0),.palette = {[0] = 0x2400,[1] = 0x2800,[2] = 0x2c00,[3] = 0x3000,[4] = 0x3400,},.has_shadowcon = 1,.has_blendcon = 1,.has_clksel = 1,.has_fixvclk = 1,},.win[0] = &s3c_fb_data_s5p_wins[0],.win[1] = &s3c_fb_data_s5p_wins[1],.win[2] = &s3c_fb_data_s5p_wins[2],.win[3] = &s3c_fb_data_s5p_wins[3],.win[4] = &s3c_fb_data_s5p_wins[4], };static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {.variant = {.nr_windows = 5,.vidtcon = VIDTCON0,.wincon = WINCON(0),.winmap = WINxMAP(0),.keycon = WKEYCON,.osd = VIDOSD_BASE,.osd_stride = 16,.buf_start = VIDW_BUF_START(0),.buf_size = VIDW_BUF_SIZE(0),.buf_end = VIDW_BUF_END(0),.palette = {[0] = 0x2400,[1] = 0x2800,[2] = 0x2c00,[3] = 0x3000,[4] = 0x3400,},.has_shadowcon = 1,.has_blendcon = 1,.has_fixvclk = 1,},.win[0] = &s3c_fb_data_s5p_wins[0],.win[1] = &s3c_fb_data_s5p_wins[1],.win[2] = &s3c_fb_data_s5p_wins[2],.win[3] = &s3c_fb_data_s5p_wins[3],.win[4] = &s3c_fb_data_s5p_wins[4], };static struct s3c_fb_driverdata s3c_fb_data_exynos5 = {.variant = {.nr_windows = 5,.vidtcon = VIDTCON0,.wincon = WINCON(0),.winmap = WINxMAP(0),.keycon = WKEYCON,.osd = VIDOSD_BASE,.osd_stride = 16,.buf_start = VIDW_BUF_START(0),.buf_size = VIDW_BUF_SIZE(0),.buf_end = VIDW_BUF_END(0),.palette = {[0] = 0x2400,[1] = 0x2800,[2] = 0x2c00,[3] = 0x3000,[4] = 0x3400,},.has_shadowcon = 1,.has_blendcon = 1,.has_fixvclk = 1,},.win[0] = &s3c_fb_data_s5p_wins[0],.win[1] = &s3c_fb_data_s5p_wins[1],.win[2] = &s3c_fb_data_s5p_wins[2],.win[3] = &s3c_fb_data_s5p_wins[3],.win[4] = &s3c_fb_data_s5p_wins[4], };/* S3C2443/S3C2416 style hardware */ static struct s3c_fb_driverdata s3c_fb_data_s3c2443 = {.variant = {.nr_windows = 2,.is_2443 = 1,.vidtcon = 0x08,.wincon = 0x14,.winmap = 0xd0,.keycon = 0xb0,.osd = 0x28,.osd_stride = 12,.buf_start = 0x64,.buf_size = 0x94,.buf_end = 0x7c,.palette = {[0] = 0x400,[1] = 0x800,},.has_clksel = 1,},.win[0] = &(struct s3c_fb_win_variant) {.palette_sz = 256,.valid_bpp = VALID_BPP1248 | VALID_BPP(16) | VALID_BPP(24),},.win[1] = &(struct s3c_fb_win_variant) {.has_osd_c = 1,.has_osd_alpha = 1,.palette_sz = 256,.valid_bpp = (VALID_BPP1248 | VALID_BPP(16) |VALID_BPP(18) | VALID_BPP(19) |VALID_BPP(24) | VALID_BPP(25) |VALID_BPP(28)),}, };static struct s3c_fb_driverdata s3c_fb_data_s5p64x0 = {.variant = {.nr_windows = 3,.vidtcon = VIDTCON0,.wincon = WINCON(0),.winmap = WINxMAP(0),.keycon = WKEYCON,.osd = VIDOSD_BASE,.osd_stride = 16,.buf_start = VIDW_BUF_START(0),.buf_size = VIDW_BUF_SIZE(0),.buf_end = VIDW_BUF_END(0),.palette = {[0] = 0x2400,[1] = 0x2800,[2] = 0x2c00,},.has_blendcon = 1,.has_fixvclk = 1,},.win[0] = &s3c_fb_data_s5p_wins[0],.win[1] = &s3c_fb_data_s5p_wins[1],.win[2] = &s3c_fb_data_s5p_wins[2], };static struct platform_device_id s3c_fb_driver_ids[] = {{.name = "s3c-fb",.driver_data = (unsigned long)&s3c_fb_data_64xx,}, {.name = "s5pc100-fb",.driver_data = (unsigned long)&s3c_fb_data_s5pc100,}, {.name = "s5pv210-fb",.driver_data = (unsigned long)&s3c_fb_data_s5pv210,}, {.name = "exynos4-fb",.driver_data = (unsigned long)&s3c_fb_data_exynos4,}, {.name = "exynos5-fb",.driver_data = (unsigned long)&s3c_fb_data_exynos5,}, {.name = "s3c2443-fb",.driver_data = (unsigned long)&s3c_fb_data_s3c2443,}, {.name = "s5p64x0-fb",.driver_data = (unsigned long)&s3c_fb_data_s5p64x0,},{}, }; MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids);static const struct dev_pm_ops s3cfb_pm_ops = {SET_SYSTEM_SLEEP_PM_OPS(s3c_fb_suspend, s3c_fb_resume)SET_RUNTIME_PM_OPS(s3c_fb_runtime_suspend, s3c_fb_runtime_resume, NULL) };static struct platform_driver s3c_fb_driver = {.probe = s3c_fb_probe,.remove = __devexit_p(s3c_fb_remove),.id_table = s3c_fb_driver_ids,.driver = {.name = "s3c-fb",.owner = THIS_MODULE,.pm = &s3cfb_pm_ops,}, };module_platform_driver(s3c_fb_driver);MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:s3c-fb");

?

總結(jié)

以上是生活随笔為你收集整理的⑥tiny4412 Linux驱动开发之LCD(framebuffer)驱动程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

国产精品久久久久久久久久久免费 | 国产系列 在线观看 | 亚洲精品国产精品99久久 | 中文字幕黄色网 | 亚洲精品美女免费 | 国产精品专区在线观看 | 精品国产1区2区3区 国产欧美精品在线观看 | 一区在线观看 | 国产精品久久久久永久免费 | 91av资源在线 | 精品国产乱码久久久久久1区二区 | 精品国产99 | 最新一区二区三区 | 7777xxxx | 99免费精品| 欧美日韩精品免费观看 | 操操综合网 | 欧美日韩精品影院 | 国产精品美女在线 | 日韩av男人的天堂 | 亚洲少妇久久 | 在线观看精品国产 | 天天要夜夜操 | 欧美极品xxxxx | 欧美日韩性视频在线 | 日韩在线欧美在线 | 96久久久 | 青青草国产在线 | 欧美有色 | 免费色视频在线 | 在线观看网站黄 | 国产精品久久久久国产精品日日 | 国产成人精品一区二区在线观看 | 天天爱天天草 | 久久免费视频这里只有精品 | 韩国av电影在线观看 | 四虎在线免费观看视频 | 日韩精品中文字幕有码 | 天天综合网天天 | 久久久久国产免费免费 | 黄色大片免费网站 | 国产精品6| 九七视频在线观看 | 成人午夜电影在线播放 | 精品无人国产偷自产在线 | 最新真实国产在线视频 | 27xxoo无遮挡动态视频 | 玖玖色在线观看 | 亚洲欧美va | 五月天激情视频在线观看 | 日韩激情视频在线 | 午夜精品中文字幕 | 日韩欧美视频免费在线观看 | 国偷自产中文字幕亚洲手机在线 | 黄色.com| 国产精品福利在线播放 | 在线天堂中文www视软件 | www.91成人 | 久热av在线 | 99操视频| 亚洲精品自在在线观看 | 日韩欧美在线播放 | 国产婷婷在线观看 | 超碰在线1 | 五月婷婷欧美视频 | 欧美日韩xxx | 欧美视频网址 | 欧美在线视频日韩 | 午夜久久久久久久久 | 免费视频一二三区 | 一区在线观看视频 | 狠狠操在线 | 91亚洲精品久久久蜜桃网站 | www五月天婷婷 | 国产精品一区二区三区免费看 | 中国一级片视频 | 国产第一页福利影院 | 午夜久操| 国产精品一区二区视频 | 欧美巨大荫蒂茸毛毛人妖 | av免费观看高清 | 在线观看免费版高清版 | 欧美色图狠狠干 | 九九爱免费视频在线观看 | 久久五月激情 | 91在线永久 | 国产精品一区二区在线观看 | 97超在线视频 | 亚洲麻豆精品 | 国产精品门事件 | 亚洲在线视频观看 | 婷婷日韩| 国产精品久久视频 | 久久精品亚洲一区二区三区观看模式 | 婷婷在线免费视频 | 99久久99久久精品 | 黄色大片日本免费大片 | 久久久久久久久久亚洲精品 | 在线免费试看 | 成 人 黄 色 片 在线播放 | 久草剧场 | 三级a毛片| 久久精品国产亚洲 | 美女黄久久 | 久久大片 | 久久人91精品久久久久久不卡 | 91成人精品| 国产视频 久久久 | 99情趣网视频| 久久久国产高清 | 色综合咪咪久久网 | 国产xx视频| 国产在线国偷精品产拍 | 国产乱对白刺激视频在线观看女王 | 五月婷婷综合激情网 | 91精品国产一区二区在线观看 | 91中文在线| a级国产乱理伦片在线播放 久久久久国产精品一区 | 久久久精选 | 久久艹99| 国产自在线 | 国内丰满少妇猛烈精品播放 | 成人免费在线播放视频 | 久久国产91 | 亚洲日韩欧美一区二区在线 | 国产精品理论片在线观看 | 91免费高清观看 | 中文字幕日韩电影 | 天天综合日 | 99国产精品一区二区 | 久久av免费| 五月天婷婷免费视频 | 国产精品毛片久久蜜 | 手机av看片 | 免费看三级网站 | 国产拍揄自揄精品视频麻豆 | 九九热视频在线免费观看 | 天天色天天上天天操 | avove黑丝 | 亚洲精品乱码久久久久久蜜桃91 | 国产涩涩网站 | 久久成人在线 | 在线观看国产v片 | 免费在线91 | 91福利免费| 国产色拍 | 日韩精品免费专区 | 黄www在线观看 | 久久www免费人成看片高清 | 精品久久毛片 | 一区免费视频 | 在线观看日韩国产 | 国产区欧美| 亚洲精品天天 | 欧美日韩视频在线观看一区二区 | 日b视频在线观看网址 | 五月综合激情婷婷 | 激情五月***国产精品 | 91中文字幕 | a天堂一码二码专区 | 久久 精品一区 | 亚洲三级国产 | 在线午夜av| 在线观看国产区 | 91精品国产成人观看 | www.com.黄| а中文在线天堂 | 国产美女搞久久 | 三上悠亚一区二区在线观看 | 国产在线更新 | 麻豆国产网站入口 | 国产一在线精品一区在线观看 | 色婷婷综合久久久 | 91chinesexxx| 婷婷四房综合激情五月 | 成人午夜精品久久久久久久3d | 999精品网 | 福利一区视频 | 亚洲专区一二三 | 久久久精品网 | 亚洲美女视频网 | 国产免费国产 | 成人av网站在线观看 | 在线观看a视频 | 欧美日本日韩aⅴ在线视频 插插插色综合 | 日韩av网页 | 欧美日韩高清在线观看 | 亚洲va在线va天堂 | 亚洲精品国产精品国自产观看浪潮 | 97国产超碰 | 久久国产精品成人免费浪潮 | 亚洲日本一区二区在线 | 免费av片在线 | 久久久久久国产一区二区三区 | 亚洲影院国产 | 国产特级毛片aaaaaa | 久久精品国产v日韩v亚洲 | 最新国产在线观看 | 日日夜夜狠狠 | 欧美一区二区在线刺激视频 | 亚洲国产精品一区二区久久hs | 操操操人人 | 婷婷久久亚洲 | 久久久久久久国产精品影院 | 日日操夜夜操狠狠操 | 日日天天av| 亚洲国产欧美在线人成大黄瓜 | 99久国产 | www.五月天婷婷 | 青青河边草手机免费 | 亚洲精品乱码白浆高清久久久久久 | 中文字幕在线一区观看 | 久久久久久激情 | 91精品中文字幕 | 国产69久久久欧美一级 | 国产精品一级在线 | 久操视频在线观看 | 国产精品免费在线观看视频 | 国产网红在线观看 | 亚洲午夜久久久影院 | 国产日韩欧美视频 | 国产精品第10页 | www.com操| 欧美福利久久 | 欧美日韩不卡在线 | 人人爱人人舔 | 亚洲精品视频在线观看免费视频 | 亚洲精品三级 | 九九热在线精品视频 | 日韩欧美精品免费 | 中文字幕欧美日韩va免费视频 | 五月婷婷欧美视频 | 成人av资源在线 | 少妇性xxx| 国产精品精 | 狠狠干天天 | 中文视频一区二区 | 久久av福利| 狠狠操天天操 | 91黄色免费网站 | 亚洲少妇久久 | 天天操综合网站 | 顶级欧美色妇4khd | 91视频下载 | 综合久久综合久久 | 久久草| 国产精品一区专区欧美日韩 | 欧日韩在线视频 | 草久在线播放 | 一级成人在线 | 免费黄色av | 国产精彩视频一区 | 天操夜夜操 | 伊人成人久久 | 996久久国产精品线观看 | 国产一级黄大片 | 97**国产露脸精品国产 | 国产高清精品在线观看 | 免费在线观看的av网站 | 99精品视频一区二区 | 国产精品涩涩屋www在线观看 | 在线国产一区 | 国产视频二区三区 | 97超碰人人澡人人爱 | 欧美污污网站 | 国产精品成人一区二区 | 天天天插 | 日韩一区二区三区高清免费看看 | 久久成人午夜视频 | 婷婷在线免费 | 日本最新中文字幕 | 人人爱人人做人人爽 | 国产精品高潮呻吟久久久久 | 99久久婷婷国产精品综合 | 国产成人精品av在线观 | 美女在线免费观看视频 | 在线观看黄| 99精品区| 精品一区二区在线看 | 婷婷色在线| 免费观看十分钟 | www.日本色 | 丁香色婷婷 | 精品国产欧美一区二区三区不卡 | 国产精品久久久久久久久久免费 | 免费一级黄色 | 中文字幕在线观看1 | 毛片网在线观看 | 久久久综合香蕉尹人综合网 | 亚洲资源在线观看 | 九色在线 | 911在线 | 国产精品你懂的在线观看 | 91人人爱 | 337p欧美 | 中文字幕一区二区三区精华液 | 中文字幕4| 激情伊人 | 国内小视频| 精品99在线视频 | 在线免费观看国产视频 | 久久久91精品国产一区二区精品 | 国产在线无 | 91视频在线观看免费 | 国产视频一级 | 黄a网 | 我爱av激情网 | 手机av在线网站 | 亚洲综合小说 | 男女激情片在线观看 | 色婷婷色 | 国产精品乱码一区二三区 | 久久久久久久久久久久久久av | 亚洲伊人成综合网 | 在线黄色观看 | 国产高清网站 | 99久久国产免费,99久久国产免费大片 | 欧美精品在线观看一区 | av三级av | 久久久综合香蕉尹人综合网 | 亚洲首页 | www日韩精品 | 免费高清无人区完整版 | 免费观看不卡av | 人人爽人人 | 黄色亚洲免费 | 五月激情亚洲 | 国产资源精品 | 二区三区av | 四虎成人精品在永久免费 | www.久久久 | 中文字幕影视 | 久久亚洲综合色 | 国产精品情侣视频 | 亚洲成人av在线电影 | 国产精品一区二区在线观看免费 | 婷婷5月色 | 久久视频在线观看 | 韩国av电影在线观看 | 韩国一区在线 | 亚州av成人 | www.天天射| 日本色小说视频 | 在线日韩中文字幕 | 色网站视频| 人人澡人摸人人添学生av | 久久久在线 | 天天干夜夜操视频 | 国产爽妇网| 色婷婷免费 | 超碰在线天天 | 天天干 天天摸 天天操 | 操操操av| 天天综合亚洲 | 国产亚洲永久域名 | 成年人黄色免费看 | 欧美国产日韩久久 | 91av播放 | 日韩中文字幕亚洲一区二区va在线 | 97在线免费| 欧美国产三区 | 国产不卡一区二区视频 | 玖玖在线精品 | 久久99婷婷| 日韩性xxxx| 99精品国产99久久久久久97 | 97av视频| 91九色porn在线资源 | 97av视频| 国产成人精品在线播放 | 日日夜夜噜噜噜 | 日韩欧美精品一区二区三区经典 | 韩国精品在线 | 91自拍视频在线 | 色av色av色av | 国产香蕉久久精品综合网 | 黄色三级网站在线观看 | av在线网站免费观看 | 成人欧美在线 | 成人a在线 | 热99在线视频 | 亚洲国产丝袜在线观看 | 日本久久久久久久久久 | 亚洲成免费 | 91视频成人免费 | 免费看黄视频 | 欧美日韩精品免费观看视频 | 精品国产欧美一区二区三区不卡 | 91精品国产综合久久久久久久 | 国产尤物在线观看 | 五月婷影院 | 99精品视频在线观看视频 | 日韩综合在线观看 | 在线v片免费观看视频 | 亚洲国产999 | 亚洲乱码久久 | 亚洲高清视频在线 | 亚洲成人av在线 | 国产精品一区二区免费在线观看 | 新版资源中文在线观看 | 国产黄在线 | 久久久久久久久久久久电影 | 色资源二区在线视频 | 国产精品久久久久永久免费看 | 99久免费精品视频在线观看 | 久久精品成人热国产成 | 四虎影视欧美 | 色婷婷综合成人av | 亚洲一区网 | 91.麻豆视频| 免费观看www视频 | 久久精品国产免费看久久精品 | 中文字幕精品一区二区精品 | 综合网色 | 色婷婷九月 | 在线播放国产一区二区三区 | 国产一区免费看 | 久久久影院一区二区三区 | 激情综合电影网 | 中文字幕在线观看网址 | 亚洲一级电影在线观看 | 亚洲在线精品 | 亚洲精品国产成人 | 91精品国产麻豆国产自产影视 | 黄色免费网站 | 美国三级黄色大片 | 日韩女同一区二区三区在线观看 | 亚洲综合在线一区二区三区 | 日本成人免费在线观看 | 999国产| 五月天婷婷丁香花 | 婷婷激情五月综合 | a视频免费在线观看 | 伊人天堂久久 | 欧美做受高潮电影o | 蜜臀久久99精品久久久无需会员 | 国产小视频免费在线网址 | 国产精品久久久久久久久久了 | www.黄色在线| 又爽又黄又刺激的视频 | 久操中文字幕在线观看 | 久久精品一区二区三区国产主播 | 午夜在线国产 | 日本不卡一区二区三区在线观看 | 99色婷婷 | 日本少妇高清做爰视频 | 免费看91的网站 | 一区二区三区高清在线 | 亚洲区另类春色综合小说校园片 | 免费av影视| www.狠狠干| 麻豆精品视频在线 | 久久欧洲视频 | 性色xxxxhd | 成人理论电影 | 久久天天躁狠狠躁亚洲综合公司 | 欧亚日韩精品一区二区在线 | 久青草视频 | 99久久精品免费看国产 | 一区二区三区四区五区六区 | 毛片3| 99精品在线视频观看 | 月丁香婷婷 | 麻豆国产视频下载 | 热久精品 | .国产精品成人自产拍在线观看6 | 四虎在线观看精品视频 | 免费av网站在线 | 亚洲aⅴ在线观看 | 久久精品婷婷 | 久久999久久| 成年人免费av | 精品在线你懂的 | 日韩不卡高清 | 日韩激情在线 | 成人午夜精品久久久久久久3d | 9色在线视频 | 干 操 插 | 免费a网 | 狠狠综合久久 | 久久久午夜电影 | 日韩视频在线不卡 | 懂色av一区二区在线播放 | 热99在线视频 | 国产麻豆精品一区二区 | 欧美地下肉体性派对 | 亚洲激情视频在线观看 | 免费黄色在线 | 日韩三级不卡 | 亚洲欧美日本A∨在线观看 青青河边草观看完整版高清 | 精选久久| 99高清视频有精品视频 | 成人免费av电影 | 九九久久成人 | 欧美在线观看视频一区二区 | 18久久久久久 | 亚洲精品在线免费播放 | 欧美成人va| 久久久久北条麻妃免费看 | 97在线精品国自产拍中文 | 国产手机在线观看 | 成人免费观看在线视频 | 久久久久久久久毛片 | 黄色一级免费电影 | 成人资源在线 | 日韩精品2区 | 少妇按摩av| 国产精品久久久久久一区二区三区 | 日韩欧美在线视频一区二区三区 | 中文字幕丰满人伦在线 | 久久精品视频一 | 亚洲精品播放 | 国产成人一二三 | 国产字幕在线观看 | 永久黄网站色视频免费观看w | 国产精品一区久久久久 | 国产精品欧美久久久久天天影视 | 91综合色 | 亚洲免费视频在线观看 | 国产一区二区影院 | 亚洲精品一区二区18漫画 | 手机在线看a | 亚洲美女视频在线 | 国产精品免费久久久久影院仙踪林 | 黄色一级免费网站 | 正在播放亚洲精品 | 一级欧美日韩 | 成人宗合网 | 国产精品视频久久 | 久久久精品小视频 | 成人免费中文字幕 | 天天干视频在线 | 超碰在线天天 | 日日夜夜精品免费观看 | 久久婷婷视频 | 国产精品午夜在线观看 | 亚州精品国产 | 丝袜av网站| 日韩国产精品久久久久久亚洲 | 在线观影网站 | 中文字幕国产精品一区二区 | 五月精品 | 国产资源在线观看 | 中文字幕一区二区三区乱码在线 | 日本aaaa级毛片在线看 | 久久久久久久久久久久久国产精品 | 一区二区三区日韩在线观看 | 精品一区二区三区电影 | 美女免费视频一区 | 久久婷婷国产 | 中文字幕在线影院 | 伊人婷婷在线 | 国产乱对白刺激视频不卡 | 国产精品久久久久久久久软件 | 日本中文字幕在线播放 | 视频在线一区 | 久久久久久久久久久久久久av | 免费av电影网站 | 亚洲最新合集 | 欧美日韩国产xxx | 国产男女爽爽爽免费视频 | 91视频在线免费观看 | 日本久久不卡视频 | 久草免费福利在线观看 | 伊人黄色网 | 深爱激情婷婷网 | 手机在线看a| 96亚洲精品久久 | 国产精品三级视频 | 天天搞天天干天天色 | 国内视频1区 | 日本久久久亚洲精品 | 色网站在线免费观看 | 91久久国产自产拍夜夜嗨 | 婷婷国产精品 | 国产小视频在线免费观看 | 亚洲精品国久久99热 | 亚洲激情网站免费观看 | 麻豆精品91 | 日韩三级视频在线观看 | 欧美日韩国产一区二区在线观看 | 在线免费观看一区二区三区 | 久久综合久久久久88 | 国产91九色视频 | 久久精品96 | 五月婷婷综合在线视频 | 91成品人影院 | www.五月婷婷 | av黄色亚洲 | 欧美性久久久 | 精品无人国产偷自产在线 | 91伊人影院 | 中文字幕欲求不满 | 欧美a级在线 | 国产精品短视频 | 中文字幕中文字幕在线中文字幕三区 | 欧美乱码精品一区二区 | 天天做夜夜做 | 国产乱码精品一区二区三区介绍 | 色天天综合久久久久综合片 | 国产韩国日本高清视频 | 狠狠色噜噜狠狠狠 | 精品国产亚洲一区二区麻豆 | 国产精品一二 | 日韩中文字幕免费视频 | 在线观看视频中文字幕 | 天天操导航 | 在线 国产 亚洲 欧美 | 精品日韩在线一区 | 久久综合之合合综合久久 | 99一区二区三区 | 特级大胆西西4444www | 日韩一三区 | 一级黄色电影网站 | 久久久久久久久久福利 | 九草在线视频 | 综合国产在线观看 | 激情婷婷丁香 | 精品伊人久久久 | 麻豆视频免费看 | 亚洲片在线观看 | 在线观看片 | 色99之美女主播在线视频 | 国产人成在线观看 | 成人a级黄色片 | 欧美少妇bbwhd | 久久99精品一区二区三区三区 | 中文字幕在线第一页 | 在线看91| 在线观看视频一区二区 | 91久久久久久久一区二区 | 欧美久久影院 | 天天草天天干天天 | 久久桃花网 | 99免费精品 | 欧美日韩视频网站 | 日韩一区二区免费视频 | 亚洲四虎 | 国产又黄又爽又猛视频日本 | 亚洲国产播放 | 在线观看国产亚洲 | 欧美二区在线播放 | 国产精品18久久久久久久久久久久 | 一本之道乱码区 | 夜又临在线观看 | 久艹视频在线免费观看 | 在线播放精品一区二区三区 | 久久激情视频 | 国产一级免费观看视频 | 成人性生活大片 | 精壮的侍卫呻吟h | 中文字幕在线观看国产 | 婷婷激情五月综合 | 日本黄色免费在线 | 国产a国产 | 日本久久99 | 午夜精品久久久久久久99无限制 | 色噜噜噜噜| 亚洲一级免费观看 | 亚洲 综合 精品 | 在线免费观看羞羞视频 | 99久热在线精品视频观看 | 私人av| 91亚·色 | 国产精品96久久久久久吹潮 | 久草视频免费 | 午夜精品久久久久久99热明星 | 麻豆视屏 | 天无日天天操天天干 | 日韩精品不卡在线 | 欧美视频一区二 | 国产一级在线看 | 懂色av一区二区在线播放 | 男女啪啪网站 | 五月综合激情网 | 久久免费成人精品视频 | 天天艹天天干天天 | 日韩精品中文字幕在线观看 | 欧美色图亚洲图片 | 国产手机视频在线观看 | 亚洲精品美女久久久久网站 | 极品久久久久 | 色欲综合视频天天天 | 国色天香永久免费 | 欧美成人免费在线 | 五月亚洲综合 | 色综合久久综合网 | 五月婷婷开心中文字幕 | 视色网站 | 色人久久| 精品一区二区三区在线播放 | 色婷婷激情| 91精品夜夜 | .国产精品成人自产拍在线观看6 | 麻豆一区在线观看 | 亚洲欧洲精品视频 | 国产在线不卡精品 | 欧美日韩二三区 | 青草视频在线看 | 国产精品入口a级 | 国产精品 美女 | 国产精品一区二区免费视频 | 天天亚洲综合 | 中文字幕一区2区3区 | 国产精品久久久影视 | 黄色软件网站在线观看 | 久久成电影| 国产日韩视频在线播放 | 日韩电影在线观看一区二区三区 | 国产精品免费大片视频 | 久久精品99国产国产精 | 久久久网站 | 国产一区在线看 | 国产精品成人a免费观看 | 人人干人人艹 | 欧美日韩久久不卡 | 一本到在线 | 波多野结衣视频一区二区三区 | 国产精品麻豆一区二区三区 | 久久夜夜夜 | 激情在线网站 | 91在线精品秘密一区二区 | 欧美a级在线免费观看 | 黄色三级在线 | 久操中文字幕在线观看 | 亚洲爱视频 | 国产裸体视频网站 | 国产精久久久久久妇女av | 99国产视频 | 99久久毛片| 婷婷爱五月天 | 国产精品欧美久久久久天天影视 | 亚洲精品在线视频网站 | 九色福利视频 | 五月婷婷六月丁香在线观看 | 国产精品免费观看久久 | 亚洲人在线7777777精品 | 久久久精品综合 | 欧美性高跟鞋xxxxhd | 2000xxx影视| 中文字幕在线观看你懂的 | 精品少妇一区二区三区在线 | 伊人手机在线 | 欧美色综合天天久久综合精品 | 九色激情网 | 日韩免费在线 | 成人毛片在线观看视频 | 五月av在线 | 97在线精品 | 国产福利专区 | 九九久久免费 | 久色小说 | 免费高清无人区完整版 | 婷婷久久亚洲 | 狠狠躁天天躁综合网 | 亚洲精品在线视频观看 | 欧美最新大片在线看 | 99久久国产免费免费 | 欧美在线观看小视频 | 四虎永久免费在线观看 | 亚洲国产精品电影在线观看 | 欧美日韩精品二区第二页 | 亚洲精品视频免费在线观看 | 最近2019年日本中文免费字幕 | 99精品乱码国产在线观看 | 日韩三级视频在线观看 | 亚洲欧美日本A∨在线观看 青青河边草观看完整版高清 | 五月婷婷伊人网 | 四虎最新入口 | 亚洲精品自在在线观看 | 在线看的毛片 | 极品美女被弄高潮视频网站 | 国产精品女 | 黄色成人毛片 | 国产老太婆免费交性大片 | 夜夜干天天操 | 日韩在线看片 | 国产黄大片| 亚洲精品视频国产 | 精品欧美一区二区精品久久 | 成人av网站在线观看 | 天天想夜夜操 | 黄色免费观看网址 | 日韩av一区在线观看 | 黄色性av| 99热精品国产一区二区在线观看 | 久久亚洲综合国产精品99麻豆的功能介绍 | 久草国产在线观看 | 视频在线观看一区 | 国产精品综合在线观看 | 综合久色 | 丝袜美腿一区 | 91av美女| 免费情缘 | 五月婷香| 免费看片黄色 | 欧美有色| 欧美性色黄大片在线观看 | 国产精品a级 | 亚洲午夜精品一区二区三区电影院 | 中文字幕在线播出 | 伊人久操| 久草在线视频看看 | 国产流白浆高潮在线观看 | 93久久精品日日躁夜夜躁欧美 | 久久精品精品电影网 | 国产999久久久 | 国产精品久久久久免费a∨ 欧美一级性生活片 | 麻豆视频在线免费 | 中文字幕在线看视频 | 国产精品不卡在线 | 成人免费视频网 | 精品一区二区免费在线观看 | 久久极品| 91大神一区二区三区 | 美女很黄免费网站 | 婷婷www| 毛片在线网| 国产v视频| 亚洲黄色免费网站 | 日韩一区二区三区视频在线 | 久精品在线 | 成人aaa毛片| 国产自产在线视频 | 日日夜夜精品网站 | 在线视频 91 | 久久综合福利 | av大全在线观看 | 久久婷婷一区二区三区 | 国产一级片久久 | 91重口视频 | 国产午夜视频在线观看 | 亚洲人人av | 精品亚洲在线 | 超碰人人做 | 国产成人精品av在线 | 国产成人精品亚洲日本在线观看 | 国产在线观看高清视频 | 日韩无在线| 亚洲黄色大片 | 97久久精品午夜一区二区 | 一二三区视频在线 | 亚洲毛片视频 | 在线观看免费国产小视频 | 日韩 在线a | 亚洲激情精品 | 久久激情婷婷 | a天堂免费 | 国产色在线,com | 国产精品国产三级国产aⅴ无密码 | 久久午夜电影 | 精品在线小视频 | 亚洲国产视频网站 | 亚洲精品女 | 国产剧情av在线播放 | 亚洲欧洲一区二区在线观看 | 操操操com| 99久久精品免费看国产一区二区三区 | 亚洲国产欧美在线人成大黄瓜 | 久久夜色精品国产欧美乱 | 激情开心站 | 视频三区在线 | 国产在线第三页 | 欧美日韩国产一区二区在线观看 | 黄色三级在线 | 亚洲免费成人av电影 | 手机在线看片日韩 | 免费av电影网站 | 国产精品久久久久久久久久久久久 | 在线之家免费在线观看电影 | 香蕉视频在线网站 | 欧美黑人性爽 | 亚洲日b视频 | www婷婷| 欧美日韩视频一区二区 | 精品一区 在线 | 亚洲最新在线 | se视频网址 | 91成人网页版 | 免费热情视频 | 国产91学生粉嫩喷水 | 亚洲三级av | 99自拍视频在线观看 | 欧美日韩精| 日日夜夜草 | 成人羞羞视频在线观看免费 | 狠狠干成人 | 国产一区二区播放 | 激情综合五月 | 国产黑丝一区二区三区 | 特级黄色视频毛片 | 在线视频一区二区 | 免费观看www小视频的软件 | 久精品视频在线 | 日韩理论在线播放 | 亚洲精品午夜久久久久久久 | 韩日精品在线 | 欧美激情综合色 | 欧美做受高潮 | 久久精品人人做人人综合老师 | 视频成人免费 | 黄色毛片视频免费 | 久久综合福利 | 黄色软件在线观看 | 国产综合激情 | 精品国产一区二区三区日日嗨 | 免费高清国产 | 99视屏| 免费看国产精品 | 久久露脸国产精品 | 成年人国产视频 | 成人九九视频 | 激情欧美一区二区免费视频 | 精品99免费视频 | av在线免费观看网站 | 亚洲国产资源 | 精品日韩在线 | 91在线精品一区二区 | 久久久免费高清视频 | 天天干人人插 | 在线不卡中文字幕播放 | 日韩经典一区二区三区 | 国产亚洲人成网站在线观看 | 久久av免费 | 国内精品99 | 精品久久久久国产免费第一页 | 四虎在线影视 | 在线观看日本高清mv视频 | 九九欧美视频 | 五月天色丁香 | 久久国内免费视频 | 中国一级特黄毛片大片久久 | 天天摸天天干天天操天天射 | 久草在线视频在线观看 | 91在线免费视频 | 久久xxxx | 热九九精品 | 五月激情丁香婷婷 | 国产伦理久久精品久久久久_ | 亚洲狠狠干 | 国产一区高清在线 | 日日操日日干 | 又黄又爽又刺激视频 | 激情综合六月 | 亚洲人视频在线 | 在线免费观看国产黄色 | 久久综合久久久 | 91色九色| 色综合久久精品 | 久热免费在线 | 欧美国产日韩一区二区 | 久久久久国产精品午夜一区 | 91激情视频在线播放 | 日韩在线观看第一页 | 久久精品小视频 | 亚洲精品在线视频网站 | 中国一级片在线观看 | 国产丝袜制服在线 | 日韩69av | 免费av网址在线观看 | 亚洲精品色视频 | 天天操天天插 | 在线欧美a| 超碰日韩在线 | 在线免费观看黄网站 | 国产精品中文字幕在线观看 | 激情综合站 | www.天天射 | a一片一级 | 亚洲在线成人精品 | 美女又爽又黄 | 国产成人香蕉 | 中文字幕精品一区久久久久 | 少妇精品久久久一区二区免费 | 亚洲小视频在线观看 | 最新国产在线观看 | 五月婷婷香蕉 | 黄色的网站免费看 | 日韩一级黄色大片 | 九九视频网站 | 91久久国产自产拍夜夜嗨 | 96久久欧美麻豆网站 | 天天亚洲综合 | 欧美精品久久人人躁人人爽 | 日韩欧美精品免费 | 欧美日韩国产在线精品 | 欧美成人按摩 | 亚洲国产欧美在线人成大黄瓜 | 亚洲成a人片在线观看网站口工 | 国产一二三区在线观看 | 在线观看一区二区视频 | 91精品国自产在线观看欧美 | 亚洲国产精品成人综合 | 国产黄色在线网站 | 草久在线观看视频 | 美女在线观看网站 | 欧美有色| 日韩成人精品一区二区三区 |