sh_mobile_lcdcfb.c revision ac751efa6a0d70f2c9daef5c7e3a92270f5c2dff
1cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm/* 2cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * SuperH Mobile LCDC Framebuffer 3cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * 4cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * Copyright (c) 2008 Magnus Damm 5cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * 6cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * This file is subject to the terms and conditions of the GNU General Public 7cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * License. See the file "COPYING" in the main directory of this archive 8cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm * for more details. 9cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm */ 10cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 11cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/kernel.h> 12cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/init.h> 13cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/delay.h> 14cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/mm.h> 15cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/clk.h> 160246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm#include <linux/pm_runtime.h> 17cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/platform_device.h> 18cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#include <linux/dma-mapping.h> 198564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm#include <linux/interrupt.h> 201c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt#include <linux/vmalloc.h> 2140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy#include <linux/ioctl.h> 225a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 23dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski#include <linux/console.h> 24225c9a8d1da274bf23efec43ec28b1c9e45e12f8Paul Mundt#include <video/sh_mobile_lcdc.h> 258564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm#include <asm/atomic.h> 26cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 276de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski#include "sh_mobile_lcdcfb.h" 286de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski 29a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define SIDE_B_OFFSET 0x1000 30a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define MIRROR_OFFSET 0x2000 31cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 32cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm/* shared registers */ 33cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDDCKR 0x410 34cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDDCKSTPR 0x414 35cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDINTR 0x468 36cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDSR 0x46c 37cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDCNT1R 0x470 38cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDCNT2R 0x474 399dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy#define _LDRCNTR 0x478 40cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDDDSR 0x47c 41cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDDWD0R 0x800 42cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDDRDR 0x840 43cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDDWAR 0x900 44cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define _LDDRAR 0x904 45cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 460246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm/* shared registers and their order for context save/restore */ 470246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic int lcdc_shared_regs[] = { 480246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm _LDDCKR, 490246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm _LDDCKSTPR, 500246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm _LDINTR, 510246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm _LDDDSR, 520246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm _LDCNT1R, 530246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm _LDCNT2R, 540246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm}; 550246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) 560246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 57d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski#define MAX_XRES 1920 58d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski#define MAX_YRES 1080 59cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 600246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { 61cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDDCKPAT1R] = 0x400, 62cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDDCKPAT2R] = 0x404, 63cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMT1R] = 0x418, 64cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMT2R] = 0x41c, 65cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMT3R] = 0x420, 66cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDDFR] = 0x424, 67cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDSM1R] = 0x428, 688564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm [LDSM2R] = 0x42c, 69cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDSA1R] = 0x430, 70cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMLSR] = 0x438, 71cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDHCNR] = 0x448, 72cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDHSYNR] = 0x44c, 73cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDVLNR] = 0x450, 74cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDVSYNR] = 0x454, 75cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDPMR] = 0x460, 766011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski [LDHAJR] = 0x4a0, 77cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}; 78cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 790246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { 80cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDDCKPAT1R] = 0x408, 81cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDDCKPAT2R] = 0x40c, 82cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMT1R] = 0x600, 83cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMT2R] = 0x604, 84cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMT3R] = 0x608, 85cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDDFR] = 0x60c, 86cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDSM1R] = 0x610, 878564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm [LDSM2R] = 0x614, 88cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDSA1R] = 0x618, 89cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDMLSR] = 0x620, 90cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDHCNR] = 0x624, 91cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDHSYNR] = 0x628, 92cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDVLNR] = 0x62c, 93cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDVSYNR] = 0x630, 94cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm [LDPMR] = 0x63c, 95cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}; 96cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 97cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define START_LCDC 0x00000001 98cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define LCDC_RESET 0x00000100 99cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define DISPLAY_BEU 0x00000008 100cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm#define LCDC_ENABLE 0x00000001 1018564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm#define LDINTR_FE 0x00000400 1029dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy#define LDINTR_VSE 0x00000200 1039dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy#define LDINTR_VEE 0x00000100 1048564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm#define LDINTR_FS 0x00000004 1059dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy#define LDINTR_VSS 0x00000002 1069dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy#define LDINTR_VES 0x00000001 107a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define LDRCNTR_SRS 0x00020000 108a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define LDRCNTR_SRC 0x00010000 109a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define LDRCNTR_MRS 0x00000002 110a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy#define LDRCNTR_MRC 0x00000001 11140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy#define LDSR_MRS 0x00000100 112cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 113c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetskistatic const struct fb_videomode default_720p = { 114c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .name = "HDMI 720p", 115c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .xres = 1280, 116c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .yres = 720, 117c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski 1185ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski .left_margin = 220, 1195ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski .right_margin = 110, 1205ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski .hsync_len = 40, 121c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski 122c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .upper_margin = 20, 123c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .lower_margin = 5, 124c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .vsync_len = 5, 125c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski 126c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .pixclock = 13468, 1275ae0cf82df212253857326a6706018eccb658683Guennadi Liakhovetski .refresh = 60, 128c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT, 1290246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm}; 1300246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 1310246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstruct sh_mobile_lcdc_priv { 1320246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm void __iomem *base; 1330246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm int irq; 1340246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm atomic_t hw_usecnt; 1350246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct device *dev; 1360246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct clk *dot_clk; 1370246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm unsigned long lddckr; 1380246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct sh_mobile_lcdc_chan ch[2]; 1396011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski struct notifier_block notifier; 1400246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm unsigned long saved_shared_regs[NR_SHARED_REGS]; 1410246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm int started; 142417d48274e755e537bae60461558c1f63a4e14deMagnus Damm int forced_bpp; /* 2 channel LCDC must share bpp setting */ 1430246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm}; 1440246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 145a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthystatic bool banked(int reg_nr) 146a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy{ 147a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy switch (reg_nr) { 148a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDMT1R: 149a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDMT2R: 150a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDMT3R: 151a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDDFR: 152a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDSM1R: 153a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDSA1R: 154a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDMLSR: 155a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDHCNR: 156a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDHSYNR: 157a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDVLNR: 158a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy case LDVSYNR: 159a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy return true; 160a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy } 161a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy return false; 162a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy} 163a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy 164cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, 165cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int reg_nr, unsigned long data) 166cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 167cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]); 168a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy if (banked(reg_nr)) 169a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + 170a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy SIDE_B_OFFSET); 171a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy} 172a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy 173a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthystatic void lcdc_write_chan_mirror(struct sh_mobile_lcdc_chan *chan, 174a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy int reg_nr, unsigned long data) 175a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy{ 176a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr] + 177a6f15ade97989d414e9bf33874c9d5d1f39808ecPhil Edworthy MIRROR_OFFSET); 178cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 179cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 180cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan, 181cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int reg_nr) 182cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 183cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]); 184cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 185cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 186cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_write(struct sh_mobile_lcdc_priv *priv, 187cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm unsigned long reg_offs, unsigned long data) 188cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 189cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm iowrite32(data, priv->base + reg_offs); 190cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 191cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 192cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv, 193cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm unsigned long reg_offs) 194cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 195cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return ioread32(priv->base + reg_offs); 196cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 197cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 198cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv, 199cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm unsigned long reg_offs, 200cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm unsigned long mask, unsigned long until) 201cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 202cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm while ((lcdc_read(priv, reg_offs) & mask) != until) 203cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm cpu_relax(); 204cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 205cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 206cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan) 207cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 208cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return chan->cfg.chan == LCDC_CHAN_SUBLCD; 209cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 210cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 211cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_sys_write_index(void *handle, unsigned long data) 212cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 213cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_chan *ch = handle; 214cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 215cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000); 216cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 217cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 218909f10de5de81668e4d0a401f3cb5ca6b8a3d20dMagnus Damm lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 219cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 220cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 221cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void lcdc_sys_write_data(void *handle, unsigned long data) 222cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 223cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_chan *ch = handle; 224cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 225cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000); 226cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 227cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 228909f10de5de81668e4d0a401f3cb5ca6b8a3d20dMagnus Damm lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 229cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 230cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 231cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic unsigned long lcdc_sys_read_data(void *handle) 232cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 233cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_chan *ch = handle; 234cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 235cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(ch->lcdc, _LDDRDR, 0x01000000); 236cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 237cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0)); 238cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm udelay(1); 239909f10de5de81668e4d0a401f3cb5ca6b8a3d20dMagnus Damm lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0); 240cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 241ec56b66fed526e3b7dd58dba8945c405448f48d1Magnus Damm return lcdc_read(ch->lcdc, _LDDRDR) & 0x3ffff; 242cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 243cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 244cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstruct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { 245cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_sys_write_index, 246cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_sys_write_data, 247cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_sys_read_data, 248cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}; 249cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 2508564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) 2518564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 2520246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm if (atomic_inc_and_test(&priv->hw_usecnt)) { 2530246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm pm_runtime_get_sync(priv->dev); 2548564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (priv->dot_clk) 2558564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm clk_enable(priv->dot_clk); 2568564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm } 2578564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 2588564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 2598564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) 2608564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 2610246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { 2628564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (priv->dot_clk) 2638564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm clk_disable(priv->dot_clk); 2640246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm pm_runtime_put(priv->dev); 2658564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm } 2668564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 2678564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 2681c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundtstatic int sh_mobile_lcdc_sginit(struct fb_info *info, 2691c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt struct list_head *pagelist) 2701c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt{ 2711c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt struct sh_mobile_lcdc_chan *ch = info->par; 2721c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT; 2731c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt struct page *page; 2741c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt int nr_pages = 0; 2751c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 2761c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt sg_init_table(ch->sglist, nr_pages_max); 2771c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 2781c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt list_for_each_entry(page, pagelist, lru) 2791c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0); 2801c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 2811c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt return nr_pages; 2821c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt} 2831c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 2848564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_deferred_io(struct fb_info *info, 2858564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm struct list_head *pagelist) 2868564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 2878564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm struct sh_mobile_lcdc_chan *ch = info->par; 288ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg; 2898564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 2908564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* enable clocks before accessing hardware */ 2918564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sh_mobile_lcdc_clk_on(ch->lcdc); 2928564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 2935c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt /* 2945c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * It's possible to get here without anything on the pagelist via 2955c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync() 2965c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * invocation. In the former case, the acceleration routines are 2975c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * stepped in to when using the framebuffer console causing the 2985c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * workqueue to be scheduled without any dirty pages on the list. 2995c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * 3005c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * Despite this, a panel update is still needed given that the 3015c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * acceleration routines have their own methods for writing in 3025c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * that still need to be updated. 3035c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * 3045c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * The fsync() and empty pagelist case could be optimized for, 3055c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * but we don't bother, as any application exhibiting such 3065c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt * behaviour is fundamentally broken anyways. 3075c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt */ 3085c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt if (!list_empty(pagelist)) { 3095c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist); 3105c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt 3115c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt /* trigger panel update */ 3125c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 313ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm if (bcfg->start_transfer) 314ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm bcfg->start_transfer(bcfg->board_data, ch, 315ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm &sh_mobile_lcdc_sys_bus_ops); 3165c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt lcdc_write_chan(ch, LDSM2R, 1); 3175c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE); 318ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm } else { 319ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm if (bcfg->start_transfer) 320ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm bcfg->start_transfer(bcfg->board_data, ch, 321ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm &sh_mobile_lcdc_sys_bus_ops); 3225c1a56b5f616f7063f91eb85f0ea209658f387dcPaul Mundt lcdc_write_chan(ch, LDSM2R, 1); 323ef61aae4ddf1dbd0e9b6ad21e2e57632a8fe76f6Magnus Damm } 3248564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 3258564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 3268564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info) 3278564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 3288564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm struct fb_deferred_io *fbdefio = info->fbdefio; 3298564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 3308564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (fbdefio) 3318564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm schedule_delayed_work(&info->deferred_work, fbdefio->delay); 3328564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 3338564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 3348564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic irqreturn_t sh_mobile_lcdc_irq(int irq, void *data) 3358564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 3368564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm struct sh_mobile_lcdc_priv *priv = data; 3372feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm struct sh_mobile_lcdc_chan *ch; 3388564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm unsigned long tmp; 3399dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy unsigned long ldintr; 3402feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm int is_sub; 3412feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm int k; 3428564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 3438564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* acknowledge interrupt */ 3449dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy ldintr = tmp = lcdc_read(priv, _LDINTR); 3459dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy /* 3469dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy * disable further VSYNC End IRQs, preserve all other enabled IRQs, 3479dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy * write 0 to bits 0-6 to ack all triggered IRQs. 3489dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy */ 3499dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy tmp &= 0xffffff00 & ~LDINTR_VEE; 3508564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm lcdc_write(priv, _LDINTR, tmp); 3518564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 3522feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm /* figure out if this interrupt is for main or sub lcd */ 3532feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm is_sub = (lcdc_read(priv, _LDSR) & (1 << 10)) ? 1 : 0; 3542feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 3559dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy /* wake up channel and disable clocks */ 3562feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 3572feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm ch = &priv->ch[k]; 3582feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 3592feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm if (!ch->enabled) 3602feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm continue; 3612feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 3629dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy /* Frame Start */ 3639dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy if (ldintr & LDINTR_FS) { 3649dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy if (is_sub == lcdc_chan_is_sublcd(ch)) { 3659dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy ch->frame_end = 1; 3669dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy wake_up(&ch->frame_end_wait); 3672feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 3689dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy sh_mobile_lcdc_clk_off(priv); 3699dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy } 3709dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy } 3719dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy 3729dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy /* VSYNC End */ 37340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy if (ldintr & LDINTR_VES) 37440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy complete(&ch->vsync_completion); 3752feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm } 3762feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 3778564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm return IRQ_HANDLED; 3788564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 3798564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 380cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv, 381cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int start) 382cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 383cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm unsigned long tmp = lcdc_read(priv, _LDCNT2R); 384cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int k; 385cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 386cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* start or stop the lcdc */ 387cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (start) 388cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDCNT2R, tmp | START_LCDC); 389cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm else 390cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC); 391cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 392cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* wait until power is applied/stopped on all channels */ 393cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 394cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled) 395cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm while (1) { 396cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3; 397cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (start && tmp == 3) 398cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 399cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!start && tmp == 0) 400cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 401cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm cpu_relax(); 402cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 403cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 404cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!start) 405cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */ 406cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 407cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 4086011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetskistatic void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch) 4096011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski{ 4101c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var; 4111c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski unsigned long h_total, hsync_pos, display_h_total; 4126011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski u32 tmp; 4136011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 4146011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp = ch->ldmt1r_value; 4156011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28; 4166011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27; 4176011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0; 4186011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0; 4196011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0; 4206011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0; 4216011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0; 4226011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDMT1R, tmp); 4236011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 4246011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski /* setup SYS bus */ 4256011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r); 4266011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r); 4276011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 4286011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski /* horizontal configuration */ 4291c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski h_total = display_var->xres + display_var->hsync_len + 4301c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski display_var->left_margin + display_var->right_margin; 4316011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp = h_total / 8; /* HTCN */ 4321c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */ 4336011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDHCNR, tmp); 4346011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 4351c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski hsync_pos = display_var->xres + display_var->right_margin; 4366011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski tmp = hsync_pos / 8; /* HSYNP */ 4371c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */ 4386011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDHSYNR, tmp); 4396011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 4406011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski /* vertical configuration */ 4411c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp = display_var->yres + display_var->vsync_len + 4421c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski display_var->upper_margin + display_var->lower_margin; /* VTLN */ 4431c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */ 4446011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDVLNR, tmp); 4456011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 4461c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp = display_var->yres + display_var->lower_margin; /* VSYNP */ 4471c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp |= display_var->vsync_len << 16; /* VSYNW */ 4486011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDVSYNR, tmp); 4496011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 4506011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski /* Adjust horizontal synchronisation for HDMI */ 4511c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski display_h_total = display_var->xres + display_var->hsync_len + 4521c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski display_var->left_margin + display_var->right_margin; 4531c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp = ((display_var->xres & 7) << 24) | 4541c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski ((display_h_total & 7) << 16) | 4551c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski ((display_var->hsync_len & 7) << 8) | 4566011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski hsync_pos; 4576011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski lcdc_write_chan(ch, LDHAJR, tmp); 4586011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski} 4596011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 460cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv) 461cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 462cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_chan *ch; 463cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_board_cfg *board_cfg; 464cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm unsigned long tmp; 465417d48274e755e537bae60461558c1f63a4e14deMagnus Damm int bpp = 0; 466cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int k, m; 467cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int ret = 0; 468cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 4698564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* enable clocks before accessing the hardware */ 470417d48274e755e537bae60461558c1f63a4e14deMagnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 471417d48274e755e537bae60461558c1f63a4e14deMagnus Damm if (priv->ch[k].enabled) { 4728564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sh_mobile_lcdc_clk_on(priv); 473417d48274e755e537bae60461558c1f63a4e14deMagnus Damm if (!bpp) 474417d48274e755e537bae60461558c1f63a4e14deMagnus Damm bpp = priv->ch[k].info->var.bits_per_pixel; 475417d48274e755e537bae60461558c1f63a4e14deMagnus Damm } 476417d48274e755e537bae60461558c1f63a4e14deMagnus Damm } 4778564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 478cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* reset */ 479cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET); 480cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0); 481cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 482cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* enable LCDC channels */ 483cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm tmp = lcdc_read(priv, _LDCNT2R); 484cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm tmp |= priv->ch[0].enabled; 485cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm tmp |= priv->ch[1].enabled; 486cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDCNT2R, tmp); 487cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 488cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* read data from external memory, avoid using the BEU for now */ 489cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU); 490cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 491cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* stop the lcdc first */ 492cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm sh_mobile_lcdc_start_stop(priv, 0); 493cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 494cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* configure clocks */ 495cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm tmp = priv->lddckr; 496cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 497cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ch = &priv->ch[k]; 498cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 499cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!priv->ch[k].enabled) 500cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm continue; 501cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 502cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm m = ch->cfg.clock_divider; 503cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!m) 504cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm continue; 505cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 506cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (m == 1) 507cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm m = 1 << 6; 508cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0); 509cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 510dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski /* FIXME: sh7724 can only use 42, 48, 54 and 60 for the divider denominator */ 5111c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski lcdc_write_chan(ch, LDDCKPAT1R, 0); 512cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1); 513cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 514cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 515cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDDCKR, tmp); 516cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 517cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* start dotclock again */ 518cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDDCKSTPR, 0); 519cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0); 520cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 5218564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* interrupts are disabled to begin with */ 522cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDINTR, 0); 523cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 524cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 525cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ch = &priv->ch[k]; 526cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 527cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!ch->enabled) 528cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm continue; 529cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 5306011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski sh_mobile_lcdc_geometry(ch); 531cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 532cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* power supply */ 533cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write_chan(ch, LDPMR, 0); 534cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 535cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm board_cfg = &ch->cfg.board_cfg; 536cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (board_cfg->setup_sys) 537cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ret = board_cfg->setup_sys(board_cfg->board_data, ch, 538cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm &sh_mobile_lcdc_sys_bus_ops); 539cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (ret) 540cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return ret; 541cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 542cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 543cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* word and long word swap */ 544417d48274e755e537bae60461558c1f63a4e14deMagnus Damm switch (bpp) { 545417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 16: 546417d48274e755e537bae60461558c1f63a4e14deMagnus Damm lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6); 547417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 548417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 24: 549417d48274e755e537bae60461558c1f63a4e14deMagnus Damm lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 7); 550417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 551417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 32: 552417d48274e755e537bae60461558c1f63a4e14deMagnus Damm lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 4); 553417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 554417d48274e755e537bae60461558c1f63a4e14deMagnus Damm } 555cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 556cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 557cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ch = &priv->ch[k]; 558cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 559cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!priv->ch[k].enabled) 560cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm continue; 561cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 562cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* set bpp format in PKF[4:0] */ 563cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm tmp = lcdc_read_chan(ch, LDDFR); 5641c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski tmp &= ~0x0001001f; 565417d48274e755e537bae60461558c1f63a4e14deMagnus Damm switch (ch->info->var.bits_per_pixel) { 566417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 16: 567417d48274e755e537bae60461558c1f63a4e14deMagnus Damm tmp |= 0x03; 568417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 569417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 24: 570417d48274e755e537bae60461558c1f63a4e14deMagnus Damm tmp |= 0x0b; 571417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 572417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 32: 573417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 574417d48274e755e537bae60461558c1f63a4e14deMagnus Damm } 575cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write_chan(ch, LDDFR, tmp); 576cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 577cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* point out our frame buffer */ 578e33afddca174171a68d57476ead8947476ab9240Paul Mundt lcdc_write_chan(ch, LDSA1R, ch->info->fix.smem_start); 579cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 580cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* set line size */ 581e33afddca174171a68d57476ead8947476ab9240Paul Mundt lcdc_write_chan(ch, LDMLSR, ch->info->fix.line_length); 582cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 5838564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* setup deferred io if SYS bus */ 5848564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm tmp = ch->cfg.sys_bus_cfg.deferred_io_msec; 5858564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (ch->ldmt1r_value & (1 << 12) && tmp) { 5868564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm ch->defio.deferred_io = sh_mobile_lcdc_deferred_io; 5878564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm ch->defio.delay = msecs_to_jiffies(tmp); 588e33afddca174171a68d57476ead8947476ab9240Paul Mundt ch->info->fbdefio = &ch->defio; 589e33afddca174171a68d57476ead8947476ab9240Paul Mundt fb_deferred_io_init(ch->info); 5908564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 5918564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* one-shot mode */ 5928564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm lcdc_write_chan(ch, LDSM1R, 1); 5938564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 5948564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* enable "Frame End Interrupt Enable" bit */ 5958564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm lcdc_write(priv, _LDINTR, LDINTR_FE); 5968564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 5978564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm } else { 5988564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* continuous read mode */ 5998564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm lcdc_write_chan(ch, LDSM1R, 0); 6008564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm } 601cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 602cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 603cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* display output */ 604cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm lcdc_write(priv, _LDCNT1R, LCDC_ENABLE); 605cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 606cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* start the lcdc */ 607cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm sh_mobile_lcdc_start_stop(priv, 1); 6088e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm priv->started = 1; 609cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 610cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* tell the board code to enable the panel */ 611cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 612cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ch = &priv->ch[k]; 61321bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm if (!ch->enabled) 61421bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm continue; 61521bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm 616cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm board_cfg = &ch->cfg.board_cfg; 6176de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski if (try_module_get(board_cfg->owner) && board_cfg->display_on) { 618c2439398170be9d7af28eb3ab59593369cb303f3Guennadi Liakhovetski board_cfg->display_on(board_cfg->board_data, ch->info); 6196de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski module_put(board_cfg->owner); 6206de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski } 621cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 622cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 623cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return 0; 624cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 625cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 626cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv) 627cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 628cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_chan *ch; 629cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_board_cfg *board_cfg; 630cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int k; 631cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 6322feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm /* clean up deferred io and ask board code to disable panel */ 633cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) { 634cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ch = &priv->ch[k]; 63521bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm if (!ch->enabled) 63621bc1f024d0d4ea43fc0f2a43504e759261c7b18Magnus Damm continue; 6378564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 6382feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm /* deferred io mode: 6392feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm * flush frame, and wait for frame end interrupt 6402feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm * clean up deferred io and enable clock 6412feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm */ 6425ef6b505d9df45558402bdb823a078840a6a26c4Guennadi Liakhovetski if (ch->info && ch->info->fbdefio) { 6432feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm ch->frame_end = 0; 644e33afddca174171a68d57476ead8947476ab9240Paul Mundt schedule_delayed_work(&ch->info->deferred_work, 0); 6452feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm wait_event(ch->frame_end_wait, ch->frame_end); 646e33afddca174171a68d57476ead8947476ab9240Paul Mundt fb_deferred_io_cleanup(ch->info); 647e33afddca174171a68d57476ead8947476ab9240Paul Mundt ch->info->fbdefio = NULL; 6482feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm sh_mobile_lcdc_clk_on(priv); 6498564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm } 6502feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 6512feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm board_cfg = &ch->cfg.board_cfg; 6526de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski if (try_module_get(board_cfg->owner) && board_cfg->display_off) { 6532feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm board_cfg->display_off(board_cfg->board_data); 6546de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski module_put(board_cfg->owner); 6556de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski } 656cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 657cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 658cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* stop the lcdc */ 6598e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm if (priv->started) { 6608e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm sh_mobile_lcdc_start_stop(priv, 0); 6618e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm priv->started = 0; 6628e9bb19ef97d6594e735bee64b6d72103e350854Magnus Damm } 663b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm 6648564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* stop clocks */ 6658564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm for (k = 0; k < ARRAY_SIZE(priv->ch); k++) 6668564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (priv->ch[k].enabled) 6678564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sh_mobile_lcdc_clk_off(priv); 668cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 669cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 670cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch) 671cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 672cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int ifm, miftyp; 673cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 674cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm switch (ch->cfg.interface_type) { 675cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case RGB8: ifm = 0; miftyp = 0; break; 676cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case RGB9: ifm = 0; miftyp = 4; break; 677cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case RGB12A: ifm = 0; miftyp = 5; break; 678cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case RGB12B: ifm = 0; miftyp = 6; break; 679cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case RGB16: ifm = 0; miftyp = 7; break; 680cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case RGB18: ifm = 0; miftyp = 10; break; 681cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case RGB24: ifm = 0; miftyp = 11; break; 682cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS8A: ifm = 1; miftyp = 0; break; 683cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS8B: ifm = 1; miftyp = 1; break; 684cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS8C: ifm = 1; miftyp = 2; break; 685cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS8D: ifm = 1; miftyp = 3; break; 686cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS9: ifm = 1; miftyp = 4; break; 687cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS12: ifm = 1; miftyp = 5; break; 688cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS16A: ifm = 1; miftyp = 7; break; 689cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS16B: ifm = 1; miftyp = 8; break; 690cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS16C: ifm = 1; miftyp = 9; break; 691cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS18: ifm = 1; miftyp = 10; break; 692cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case SYS24: ifm = 1; miftyp = 11; break; 693cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm default: goto bad; 694cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 695cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 696cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* SUBLCD only supports SYS interface */ 697cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (lcdc_chan_is_sublcd(ch)) { 698cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (ifm == 0) 699cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm goto bad; 700cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm else 701cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ifm = 0; 702cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 703cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 704cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm ch->ldmt1r_value = (ifm << 12) | miftyp; 705cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return 0; 706cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm bad: 707cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return -EINVAL; 708cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 709cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 710b51339fff240ff179730f8963a758147fd60f3ecMagnus Dammstatic int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, 711b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm int clock_source, 712cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_priv *priv) 713cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 714cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm char *str; 715cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int icksel; 716cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 717cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm switch (clock_source) { 718cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break; 719cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break; 720cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break; 721cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm default: 722cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return -EINVAL; 723cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 724cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 725cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm priv->lddckr = icksel << 16; 726cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 727cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (str) { 728b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm priv->dot_clk = clk_get(&pdev->dev, str); 729b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm if (IS_ERR(priv->dot_clk)) { 730b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm dev_err(&pdev->dev, "cannot get dot clock %s\n", str); 731b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm return PTR_ERR(priv->dot_clk); 732cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 733cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 7340246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 7350246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm /* Runtime PM support involves two step for this driver: 7360246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm * 1) Enable Runtime PM 7370246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm * 2) Force Runtime PM Resume since hardware is accessed from probe() 7380246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm */ 7398bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski priv->dev = &pdev->dev; 7400246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm pm_runtime_enable(priv->dev); 7410246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm pm_runtime_resume(priv->dev); 742cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return 0; 743cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 744cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 745cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_setcolreg(u_int regno, 746cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm u_int red, u_int green, u_int blue, 747cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm u_int transp, struct fb_info *info) 748cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 749cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm u32 *palette = info->pseudo_palette; 750cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 751cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (regno >= PALETTE_NR) 752cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return -EINVAL; 753cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 754cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm /* only FB_VISUAL_TRUECOLOR supported */ 755cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 756cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm red >>= 16 - info->var.red.length; 757cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm green >>= 16 - info->var.green.length; 758cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm blue >>= 16 - info->var.blue.length; 759cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm transp >>= 16 - info->var.transp.length; 760cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 761cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm palette[regno] = (red << info->var.red.offset) | 762cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm (green << info->var.green.offset) | 763cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm (blue << info->var.blue.offset) | 764cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm (transp << info->var.transp.offset); 765cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 766cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return 0; 767cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 768cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 769cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic struct fb_fix_screeninfo sh_mobile_lcdc_fix = { 770cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .id = "SH Mobile LCDC", 771cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .type = FB_TYPE_PACKED_PIXELS, 772cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .visual = FB_VISUAL_TRUECOLOR, 773cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .accel = FB_ACCEL_NONE, 7749dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy .xpanstep = 0, 7759dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy .ypanstep = 1, 7769dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy .ywrapstep = 0, 777cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}; 778cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 7798564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_fillrect(struct fb_info *info, 7808564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm const struct fb_fillrect *rect) 7818564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 7828564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sys_fillrect(info, rect); 7838564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sh_mobile_lcdc_deferred_io_touch(info); 7848564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 7858564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 7868564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_copyarea(struct fb_info *info, 7878564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm const struct fb_copyarea *area) 7888564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 7898564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sys_copyarea(info, area); 7908564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sh_mobile_lcdc_deferred_io_touch(info); 7918564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 7928564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 7938564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Dammstatic void sh_mobile_lcdc_imageblit(struct fb_info *info, 7948564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm const struct fb_image *image) 7958564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm{ 7968564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sys_imageblit(info, image); 7978564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sh_mobile_lcdc_deferred_io_touch(info); 7988564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm} 7998564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 8009dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthystatic int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var, 8019dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy struct fb_info *info) 8029dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy{ 8039dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy struct sh_mobile_lcdc_chan *ch = info->par; 80492e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy struct sh_mobile_lcdc_priv *priv = ch->lcdc; 80592e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy unsigned long ldrcntr; 80692e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy unsigned long new_pan_offset; 80792e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy 80892e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy new_pan_offset = (var->yoffset * info->fix.line_length) + 80992e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy (var->xoffset * (info->var.bits_per_pixel / 8)); 8109dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy 81192e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy if (new_pan_offset == ch->pan_offset) 8129dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy return 0; /* No change, do nothing */ 8139dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy 81492e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy ldrcntr = lcdc_read(priv, _LDRCNTR); 8159dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy 81692e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy /* Set the source address for the next refresh */ 81792e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy lcdc_write_chan_mirror(ch, LDSA1R, ch->dma_handle + new_pan_offset); 81892e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy if (lcdc_chan_is_sublcd(ch)) 81992e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_SRS); 82092e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy else 82192e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy lcdc_write(ch->lcdc, _LDRCNTR, ldrcntr ^ LDRCNTR_MRS); 82292e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy 82392e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy ch->pan_offset = new_pan_offset; 82492e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy 82592e1f9a7ed613b36f3aaf8b04a79e2fd4fa37ec1Phil Edworthy sh_mobile_lcdc_deferred_io_touch(info); 8269dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy 8279dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy return 0; 8289dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy} 8299dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy 83040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthystatic int sh_mobile_wait_for_vsync(struct fb_info *info) 83140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy{ 83240331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy struct sh_mobile_lcdc_chan *ch = info->par; 83340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy unsigned long ldintr; 83440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy int ret; 83540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 83640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy /* Enable VSync End interrupt */ 83740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy ldintr = lcdc_read(ch->lcdc, _LDINTR); 83840331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy ldintr |= LDINTR_VEE; 83940331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy lcdc_write(ch->lcdc, _LDINTR, ldintr); 84040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 84140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion, 84240331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy msecs_to_jiffies(100)); 84340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy if (!ret) 84440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy return -ETIMEDOUT; 84540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 84640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy return 0; 84740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy} 84840331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 84940331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthystatic int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd, 85040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy unsigned long arg) 85140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy{ 85240331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy int retval; 85340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 85440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy switch (cmd) { 85540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy case FBIO_WAITFORVSYNC: 85640331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy retval = sh_mobile_wait_for_vsync(info); 85740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy break; 85840331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 85940331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy default: 86040331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy retval = -ENOIOCTLCMD; 86140331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy break; 86240331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy } 86340331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy return retval; 86440331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy} 86540331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 866dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic void sh_mobile_fb_reconfig(struct fb_info *info) 867dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{ 868dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski struct sh_mobile_lcdc_chan *ch = info->par; 869dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski struct fb_videomode mode1, mode2; 870dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski struct fb_event event; 871dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski int evnt = FB_EVENT_MODE_CHANGE_ALL; 872dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 873dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski if (ch->use_count > 1 || (ch->use_count == 1 && !info->fbcon_par)) 874dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski /* More framebuffer users are active */ 875dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski return; 876dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 877dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski fb_var_to_videomode(&mode1, &ch->display_var); 878dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski fb_var_to_videomode(&mode2, &info->var); 879dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 880dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski if (fb_mode_is_equal(&mode1, &mode2)) 881dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski return; 882dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 883dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski /* Display has been re-plugged, framebuffer is free now, reconfigure */ 884dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski if (fb_set_var(info, &ch->display_var) < 0) 885dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski /* Couldn't reconfigure, hopefully, can continue as before */ 886dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski return; 887dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 888cc267ec5dfa29eba34cbf4eae3e5db9ca499c179Arnd Hannemann info->fix.line_length = mode1.xres * (ch->cfg.bpp / 8); 889dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 890dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski /* 891dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski * fb_set_var() calls the notifier change internally, only if 892dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski * FBINFO_MISC_USEREVENT flag is set. Since we do not want to fake a 893dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski * user event, we have to call the chain ourselves. 894dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski */ 895dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski event.info = info; 896cc267ec5dfa29eba34cbf4eae3e5db9ca499c179Arnd Hannemann event.data = &mode1; 897dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski fb_notifier_call_chain(evnt, &event); 898dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski} 899dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 900dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski/* 901dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski * Locking: both .fb_release() and .fb_open() are called with info->lock held if 902dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski * user == 1, or with console sem held, if user == 0. 903dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski */ 904dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic int sh_mobile_release(struct fb_info *info, int user) 905dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{ 906dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski struct sh_mobile_lcdc_chan *ch = info->par; 907dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 908dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski mutex_lock(&ch->open_lock); 909dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count); 910dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 911dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski ch->use_count--; 912dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 913dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski /* Nothing to reconfigure, when called from fbcon */ 914dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski if (user) { 915ac751efa6a0d70f2c9daef5c7e3a92270f5c2dffTorben Hohn console_lock(); 916dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski sh_mobile_fb_reconfig(info); 917ac751efa6a0d70f2c9daef5c7e3a92270f5c2dffTorben Hohn console_unlock(); 918dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski } 919dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 920dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski mutex_unlock(&ch->open_lock); 921dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 922dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski return 0; 923dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski} 924dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 925dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic int sh_mobile_open(struct fb_info *info, int user) 926dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{ 927dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski struct sh_mobile_lcdc_chan *ch = info->par; 928dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 929dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski mutex_lock(&ch->open_lock); 930dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski ch->use_count++; 931dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 932dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski dev_dbg(info->dev, "%s(): %d users\n", __func__, ch->use_count); 933dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski mutex_unlock(&ch->open_lock); 934dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 935dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski return 0; 936dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski} 937dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 938dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetskistatic int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 939dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski{ 940dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski struct sh_mobile_lcdc_chan *ch = info->par; 941417d48274e755e537bae60461558c1f63a4e14deMagnus Damm struct sh_mobile_lcdc_priv *p = ch->lcdc; 942dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 943d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski if (var->xres > MAX_XRES || var->yres > MAX_YRES || 944dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { 945830539d14379d0f5cb07832a3e4466418011f843Paul Mundt dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n", 946d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski var->left_margin, var->xres, var->right_margin, var->hsync_len, 947d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski var->upper_margin, var->yres, var->lower_margin, var->vsync_len, 948d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski PICOS2KHZ(var->pixclock)); 949dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski return -EINVAL; 950dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski } 951417d48274e755e537bae60461558c1f63a4e14deMagnus Damm 952417d48274e755e537bae60461558c1f63a4e14deMagnus Damm /* only accept the forced_bpp for dual channel configurations */ 953417d48274e755e537bae60461558c1f63a4e14deMagnus Damm if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel) 954417d48274e755e537bae60461558c1f63a4e14deMagnus Damm return -EINVAL; 955417d48274e755e537bae60461558c1f63a4e14deMagnus Damm 956417d48274e755e537bae60461558c1f63a4e14deMagnus Damm switch (var->bits_per_pixel) { 957417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 16: /* PKF[4:0] = 00011 - RGB 565 */ 958417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 24: /* PKF[4:0] = 01011 - RGB 888 */ 959417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 32: /* PKF[4:0] = 00000 - RGBA 888 */ 960417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 961417d48274e755e537bae60461558c1f63a4e14deMagnus Damm default: 962417d48274e755e537bae60461558c1f63a4e14deMagnus Damm return -EINVAL; 963417d48274e755e537bae60461558c1f63a4e14deMagnus Damm } 964417d48274e755e537bae60461558c1f63a4e14deMagnus Damm 965dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski return 0; 966dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski} 96740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy 968cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic struct fb_ops sh_mobile_lcdc_ops = { 9699dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy .owner = THIS_MODULE, 970cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .fb_setcolreg = sh_mobile_lcdc_setcolreg, 9712540c111ead82cad605ec2b14a1905ad914cc124Magnus Damm .fb_read = fb_sys_read, 9722540c111ead82cad605ec2b14a1905ad914cc124Magnus Damm .fb_write = fb_sys_write, 9738564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm .fb_fillrect = sh_mobile_lcdc_fillrect, 9748564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm .fb_copyarea = sh_mobile_lcdc_copyarea, 9758564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm .fb_imageblit = sh_mobile_lcdc_imageblit, 9769dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy .fb_pan_display = sh_mobile_fb_pan_display, 97740331b21f5fdb746e80fc609ef60ef71b5cd47d9Phil Edworthy .fb_ioctl = sh_mobile_ioctl, 978dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski .fb_open = sh_mobile_open, 979dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski .fb_release = sh_mobile_release, 980dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski .fb_check_var = sh_mobile_check_var, 981cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}; 982cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 983cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp) 984cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 985cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm switch (bpp) { 986cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case 16: /* PKF[4:0] = 00011 - RGB 565 */ 987cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->red.offset = 11; 988cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->red.length = 5; 989cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->green.offset = 5; 990cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->green.length = 6; 991cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->blue.offset = 0; 992cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->blue.length = 5; 993cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->transp.offset = 0; 994cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->transp.length = 0; 995cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 996cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 997417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 24: /* PKF[4:0] = 01011 - RGB 888 */ 998417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->red.offset = 16; 999cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->red.length = 8; 1000417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->green.offset = 8; 1001cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->green.length = 8; 1002417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->blue.offset = 0; 1003cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->blue.length = 8; 1004cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->transp.offset = 0; 1005cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->transp.length = 0; 1006cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 1007417d48274e755e537bae60461558c1f63a4e14deMagnus Damm 1008417d48274e755e537bae60461558c1f63a4e14deMagnus Damm case 32: /* PKF[4:0] = 00000 - RGBA 888 */ 1009417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->red.offset = 16; 1010417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->red.length = 8; 1011417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->green.offset = 8; 1012417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->green.length = 8; 1013417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->blue.offset = 0; 1014417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->blue.length = 8; 1015417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->transp.offset = 24; 1016417d48274e755e537bae60461558c1f63a4e14deMagnus Damm var->transp.length = 8; 1017417d48274e755e537bae60461558c1f63a4e14deMagnus Damm break; 1018cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm default: 1019cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return -EINVAL; 1020cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1021cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->bits_per_pixel = bpp; 1022cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->red.msb_right = 0; 1023cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->green.msb_right = 0; 1024cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->blue.msb_right = 0; 1025cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm var->transp.msb_right = 0; 1026cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return 0; 1027cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 1028cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 10292feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Dammstatic int sh_mobile_lcdc_suspend(struct device *dev) 10302feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm{ 10312feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm struct platform_device *pdev = to_platform_device(dev); 10322feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 10332feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm sh_mobile_lcdc_stop(platform_get_drvdata(pdev)); 10342feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm return 0; 10352feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm} 10362feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 10372feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Dammstatic int sh_mobile_lcdc_resume(struct device *dev) 10382feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm{ 10392feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm struct platform_device *pdev = to_platform_device(dev); 10402feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 10412feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); 10422feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm} 10432feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 10440246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic int sh_mobile_lcdc_runtime_suspend(struct device *dev) 10450246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm{ 10460246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct platform_device *pdev = to_platform_device(dev); 10470246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); 10480246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct sh_mobile_lcdc_chan *ch; 10490246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm int k, n; 10500246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 10510246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm /* save per-channel registers */ 10520246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm for (k = 0; k < ARRAY_SIZE(p->ch); k++) { 10530246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm ch = &p->ch[k]; 10540246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm if (!ch->enabled) 10550246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm continue; 10560246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm for (n = 0; n < NR_CH_REGS; n++) 10570246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm ch->saved_ch_regs[n] = lcdc_read_chan(ch, n); 10580246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm } 10590246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 10600246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm /* save shared registers */ 10610246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm for (n = 0; n < NR_SHARED_REGS; n++) 10620246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]); 10630246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 10640246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm /* turn off LCDC hardware */ 10650246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm lcdc_write(p, _LDCNT1R, 0); 10660246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm return 0; 10670246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm} 10680246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 10690246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Dammstatic int sh_mobile_lcdc_runtime_resume(struct device *dev) 10700246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm{ 10710246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct platform_device *pdev = to_platform_device(dev); 10720246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); 10730246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm struct sh_mobile_lcdc_chan *ch; 10740246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm int k, n; 10750246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 10760246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm /* restore per-channel registers */ 10770246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm for (k = 0; k < ARRAY_SIZE(p->ch); k++) { 10780246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm ch = &p->ch[k]; 10790246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm if (!ch->enabled) 10800246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm continue; 10810246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm for (n = 0; n < NR_CH_REGS; n++) 10820246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm lcdc_write_chan(ch, n, ch->saved_ch_regs[n]); 10830246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm } 10840246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 10850246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm /* restore shared registers */ 10860246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm for (n = 0; n < NR_SHARED_REGS; n++) 10870246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]); 10880246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 10890246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm return 0; 10900246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm} 10910246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 1092471452104b8520337ae2fb48c4e61cd4896e025dAlexey Dobriyanstatic const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { 10932feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm .suspend = sh_mobile_lcdc_suspend, 10942feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm .resume = sh_mobile_lcdc_resume, 10950246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm .runtime_suspend = sh_mobile_lcdc_runtime_suspend, 10960246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm .runtime_resume = sh_mobile_lcdc_runtime_resume, 10972feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm}; 10982feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm 10996de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski/* locking: called with info->lock held */ 11006011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetskistatic int sh_mobile_lcdc_notify(struct notifier_block *nb, 11016011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski unsigned long action, void *data) 11026011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski{ 11036011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski struct fb_event *event = data; 11046011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski struct fb_info *info = event->info; 11056011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski struct sh_mobile_lcdc_chan *ch = info->par; 11066011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg; 1107afe417c0355154c8b2547619771d6053b3c0aad7Guennadi Liakhovetski int ret; 11086011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 11096011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski if (&ch->lcdc->notifier != nb) 1110baf163749952ca5e33dd2d6a74da023e385c3a00Guennadi Liakhovetski return NOTIFY_DONE; 11116011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 11126011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski dev_dbg(info->dev, "%s(): action = %lu, data = %p\n", 11136011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski __func__, action, event->data); 11146011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 11156011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski switch(action) { 11166011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski case FB_EVENT_SUSPEND: 11176de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski if (try_module_get(board_cfg->owner) && board_cfg->display_off) { 11186011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski board_cfg->display_off(board_cfg->board_data); 11196de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski module_put(board_cfg->owner); 11206de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski } 11216011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski pm_runtime_put(info->device); 1122afe417c0355154c8b2547619771d6053b3c0aad7Guennadi Liakhovetski sh_mobile_lcdc_stop(ch->lcdc); 11236011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski break; 11246011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski case FB_EVENT_RESUME: 1125dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski mutex_lock(&ch->open_lock); 1126dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski sh_mobile_fb_reconfig(info); 1127dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski mutex_unlock(&ch->open_lock); 11286011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 11296011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski /* HDMI must be enabled before LCDC configuration */ 11306de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski if (try_module_get(board_cfg->owner) && board_cfg->display_on) { 1131dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski board_cfg->display_on(board_cfg->board_data, info); 11326de9edd5bde0cdfea12e9948690e53ec669c3018Guennadi Liakhovetski module_put(board_cfg->owner); 11336011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski } 11346011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 1135afe417c0355154c8b2547619771d6053b3c0aad7Guennadi Liakhovetski ret = sh_mobile_lcdc_start(ch->lcdc); 1136afe417c0355154c8b2547619771d6053b3c0aad7Guennadi Liakhovetski if (!ret) 1137afe417c0355154c8b2547619771d6053b3c0aad7Guennadi Liakhovetski pm_runtime_get_sync(info->device); 11386011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski } 11396011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 1140baf163749952ca5e33dd2d6a74da023e385c3a00Guennadi Liakhovetski return NOTIFY_OK; 11416011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski} 11426011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 1143cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_remove(struct platform_device *pdev); 1144cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1145c2e13037e6794bd0d9de3f9ecabf5615f15c160bUwe Kleine-Königstatic int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) 1146cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 1147cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct fb_info *info; 1148cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_priv *priv; 114901ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski struct sh_mobile_lcdc_info *pdata = pdev->dev.platform_data; 1150cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct resource *res; 1151cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int error; 1152cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm void *buf; 1153cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int i, j; 1154cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 115501ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski if (!pdata) { 1156cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "no platform data defined\n"); 11578bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski return -EINVAL; 1158cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1159cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1160cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 11618564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm i = platform_get_irq(pdev, 0); 11628564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (!res || i < 0) { 11638564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm dev_err(&pdev->dev, "cannot get platform resources\n"); 11648bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski return -ENOENT; 1165cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1166cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1167cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm priv = kzalloc(sizeof(*priv), GFP_KERNEL); 1168cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!priv) { 1169cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "cannot allocate device data\n"); 11708bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski return -ENOMEM; 1171cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1172cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 11738bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski platform_set_drvdata(pdev, priv); 11748bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski 11758564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED, 11767ad33e74857f16f1202cbc5746faf52e88e8b376Kay Sievers dev_name(&pdev->dev), priv); 11778564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (error) { 11788564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm dev_err(&pdev->dev, "unable to request irq\n"); 11798564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm goto err1; 11808564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm } 11818564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 11828564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm priv->irq = i; 11835ef6b505d9df45558402bdb823a078840a6a26c4Guennadi Liakhovetski atomic_set(&priv->hw_usecnt, -1); 1184cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1185cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm j = 0; 1186cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) { 118701ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski struct sh_mobile_lcdc_chan *ch = priv->ch + j; 1188cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 118901ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski ch->lcdc = priv; 119001ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i])); 1191cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 119201ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski error = sh_mobile_lcdc_check_interface(ch); 1193cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (error) { 1194cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "unsupported interface type\n"); 1195cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm goto err1; 1196cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 119701ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski init_waitqueue_head(&ch->frame_end_wait); 119801ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski init_completion(&ch->vsync_completion); 119901ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski ch->pan_offset = 0; 1200cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1201cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm switch (pdata->ch[i].chan) { 1202cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case LCDC_CHAN_MAINLCD: 120301ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski ch->enabled = 1 << 1; 120401ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski ch->reg_offs = lcdc_offs_mainlcd; 1205cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm j++; 1206cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 1207cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm case LCDC_CHAN_SUBLCD: 120801ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski ch->enabled = 1 << 2; 120901ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski ch->reg_offs = lcdc_offs_sublcd; 1210cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm j++; 1211cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 1212cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1213cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1214cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1215cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!j) { 1216cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "no channels defined\n"); 1217cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm error = -EINVAL; 1218cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm goto err1; 1219cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1220cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1221417d48274e755e537bae60461558c1f63a4e14deMagnus Damm /* for dual channel LCDC (MAIN + SUB) force shared bpp setting */ 1222417d48274e755e537bae60461558c1f63a4e14deMagnus Damm if (j == 2) 1223417d48274e755e537bae60461558c1f63a4e14deMagnus Damm priv->forced_bpp = pdata->ch[0].bpp; 1224417d48274e755e537bae60461558c1f63a4e14deMagnus Damm 1225dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski priv->base = ioremap_nocache(res->start, resource_size(res)); 1226dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski if (!priv->base) 1227dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski goto err1; 1228dba6f385b83d7f19eb1d4df12f422bab945c7f10Guennadi Liakhovetski 1229b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv); 1230cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (error) { 1231cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "unable to setup clocks\n"); 1232cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm goto err1; 1233cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1234cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1235cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (i = 0; i < j; i++) { 12366011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski struct fb_var_screeninfo *var; 123771d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski const struct fb_videomode *lcd_cfg, *max_cfg = NULL; 123801ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski struct sh_mobile_lcdc_chan *ch = priv->ch + i; 1239c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg; 1240c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski const struct fb_videomode *mode = cfg->lcd_cfg; 124171d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski unsigned long max_size = 0; 124271d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski int k; 12435fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski int num_cfg; 1244cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 124501ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski ch->info = framebuffer_alloc(0, &pdev->dev); 124601ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski if (!ch->info) { 1247e33afddca174171a68d57476ead8947476ab9240Paul Mundt dev_err(&pdev->dev, "unable to allocate fb_info\n"); 1248e33afddca174171a68d57476ead8947476ab9240Paul Mundt error = -ENOMEM; 1249e33afddca174171a68d57476ead8947476ab9240Paul Mundt break; 1250e33afddca174171a68d57476ead8947476ab9240Paul Mundt } 1251e33afddca174171a68d57476ead8947476ab9240Paul Mundt 125201ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski info = ch->info; 12536011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski var = &info->var; 1254cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm info->fbops = &sh_mobile_lcdc_ops; 1255c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski info->par = ch; 1256dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 1257dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski mutex_init(&ch->open_lock); 1258dd210503b77ae04adfdb25ca45536c4f7e33edb1Guennadi Liakhovetski 1259c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski for (k = 0, lcd_cfg = mode; 1260c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski k < cfg->num_cfg && lcd_cfg; 126171d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski k++, lcd_cfg++) { 126271d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski unsigned long size = lcd_cfg->yres * lcd_cfg->xres; 126371d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski 126471d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski if (size > max_size) { 126571d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski max_cfg = lcd_cfg; 126671d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski max_size = size; 126771d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski } 126871d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski } 126971d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski 1270c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski if (!mode) 1271d2ecbab5960d9814a269d36723647d6ef391ba8fGuennadi Liakhovetski max_size = MAX_XRES * MAX_YRES; 1272c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski else if (max_cfg) 1273c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n", 1274c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski max_cfg->xres, max_cfg->yres); 127571d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski 1276cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm info->fix = sh_mobile_lcdc_fix; 127771d3b0fcadf70d0de1ad334f48c9a4060209091aGuennadi Liakhovetski info->fix.smem_len = max_size * (cfg->bpp / 8) * 2; 1278cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 12795fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski if (!mode) { 1280c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski mode = &default_720p; 12815fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski num_cfg = 1; 12825fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski } else { 1283e0b9fb26266778cc749365b98041c5b7ef6f10f8Guennadi Liakhovetski num_cfg = cfg->num_cfg; 12845fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski } 12855fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski 12865fd284e6cd39f731db86dfd2440553365d5fad4dGuennadi Liakhovetski fb_videomode_to_modelist(mode, num_cfg, &info->modelist); 1287c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski 1288c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski fb_videomode_to_var(var, mode); 1289e0b9fb26266778cc749365b98041c5b7ef6f10f8Guennadi Liakhovetski var->width = cfg->lcd_size_cfg.width; 1290e0b9fb26266778cc749365b98041c5b7ef6f10f8Guennadi Liakhovetski var->height = cfg->lcd_size_cfg.height; 12919dd38819c2257375ea05bcb92b1f607a1d523c84Phil Edworthy /* Default Y virtual resolution is 2x panel size */ 12926011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski var->yres_virtual = var->yres * 2; 12936011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski var->activate = FB_ACTIVATE_NOW; 12946011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 12956011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski error = sh_mobile_lcdc_set_bpp(var, cfg->bpp); 1296cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (error) 1297cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 1298cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1299cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len, 130001ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski &ch->dma_handle, GFP_KERNEL); 1301cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (!buf) { 1302cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "unable to allocate buffer\n"); 1303cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm error = -ENOMEM; 1304cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 1305cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1306cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 130701ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski info->pseudo_palette = &ch->pseudo_palette; 1308cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm info->flags = FBINFO_FLAG_DEFAULT; 1309cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1310cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0); 1311cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (error < 0) { 1312cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "unable to allocate cmap\n"); 1313cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dma_free_coherent(&pdev->dev, info->fix.smem_len, 131401ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski buf, ch->dma_handle); 1315cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm break; 1316cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1317cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 131801ac25b59f08c0bb56dd301f024eabd542205a42Guennadi Liakhovetski info->fix.smem_start = ch->dma_handle; 1319c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski info->fix.line_length = var->xres * (cfg->bpp / 8); 1320cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm info->screen_base = buf; 1321cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm info->device = &pdev->dev; 13221c120deb60edd4c19a2109daa98f65f2ad3b9c06Guennadi Liakhovetski ch->display_var = *var; 1323cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1324cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1325cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (error) 1326cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm goto err1; 1327cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1328cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm error = sh_mobile_lcdc_start(priv); 1329cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (error) { 1330cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_err(&pdev->dev, "unable to start hardware\n"); 1331cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm goto err1; 1332cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1333cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1334cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (i = 0; i < j; i++) { 13351c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt struct sh_mobile_lcdc_chan *ch = priv->ch + i; 13361c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 1337e33afddca174171a68d57476ead8947476ab9240Paul Mundt info = ch->info; 13381c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 13391c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt if (info->fbdefio) { 13408bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski ch->sglist = vmalloc(sizeof(struct scatterlist) * 13411c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt info->fix.smem_len >> PAGE_SHIFT); 13428bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski if (!ch->sglist) { 13431c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt dev_err(&pdev->dev, "cannot allocate sglist\n"); 13441c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt goto err1; 13451c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt } 13461c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt } 13471c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 13481c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt error = register_framebuffer(info); 1349cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (error < 0) 1350cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm goto err1; 1351cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1352cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm dev_info(info->dev, 1353cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm "registered %s/%s as %dx%d %dbpp.\n", 1354cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm pdev->name, 13551c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt (ch->cfg.chan == LCDC_CHAN_MAINLCD) ? 1356cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm "mainlcd" : "sublcd", 1357c44f9f76d26c3b5158c65201d30e96393efe2fbdGuennadi Liakhovetski info->var.xres, info->var.yres, 13581c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt ch->cfg.bpp); 13598564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm 13608564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm /* deferred io mode: disable clock to save power */ 13616011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED) 13628564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm sh_mobile_lcdc_clk_off(priv); 1363cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1364cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 13656011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski /* Failure ignored */ 13666011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski priv->notifier.notifier_call = sh_mobile_lcdc_notify; 13676011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski fb_register_client(&priv->notifier); 13686011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 1369cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return 0; 13708bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetskierr1: 1371cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm sh_mobile_lcdc_remove(pdev); 13728bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski 1373cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return error; 1374cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 1375cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1376cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int sh_mobile_lcdc_remove(struct platform_device *pdev) 1377cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 1378cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev); 1379cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm struct fb_info *info; 1380cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm int i; 1381cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 13826011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski fb_unregister_client(&priv->notifier); 13836011bdeaa6089d49c02de69f05980da7bad314abGuennadi Liakhovetski 1384cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (i = 0; i < ARRAY_SIZE(priv->ch); i++) 13858bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski if (priv->ch[i].info && priv->ch[i].info->dev) 1386e33afddca174171a68d57476ead8947476ab9240Paul Mundt unregister_framebuffer(priv->ch[i].info); 1387cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1388cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm sh_mobile_lcdc_stop(priv); 1389cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1390cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm for (i = 0; i < ARRAY_SIZE(priv->ch); i++) { 1391e33afddca174171a68d57476ead8947476ab9240Paul Mundt info = priv->ch[i].info; 1392cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1393e33afddca174171a68d57476ead8947476ab9240Paul Mundt if (!info || !info->device) 1394cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm continue; 1395cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 13961c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt if (priv->ch[i].sglist) 13971c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt vfree(priv->ch[i].sglist); 13981c6a307a54668eda556f499c94e75086aaf8f80fPaul Mundt 13991ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm if (info->screen_base) 14001ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm dma_free_coherent(&pdev->dev, info->fix.smem_len, 14011ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm info->screen_base, 14021ffbb037d8e81ba4f09901451b39c8f178b05559Magnus Damm priv->ch[i].dma_handle); 1403cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm fb_dealloc_cmap(&info->cmap); 1404e33afddca174171a68d57476ead8947476ab9240Paul Mundt framebuffer_release(info); 1405cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm } 1406cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1407b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm if (priv->dot_clk) 1408b51339fff240ff179730f8963a758147fd60f3ecMagnus Damm clk_put(priv->dot_clk); 14090246c4712c40294bd5e8335f0c15a38c8e52709fMagnus Damm 14108bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski if (priv->dev) 14118bed90557d2600d25e58de30df48b244980164ecGuennadi Liakhovetski pm_runtime_disable(priv->dev); 1412cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1413cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm if (priv->base) 1414cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm iounmap(priv->base); 1415cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 14168564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm if (priv->irq) 14178564557a03c12adb9c4b76ae1e86db4113a04d13Magnus Damm free_irq(priv->irq, priv); 1418cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm kfree(priv); 1419cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return 0; 1420cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 1421cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1422cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic struct platform_driver sh_mobile_lcdc_driver = { 1423cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .driver = { 1424cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .name = "sh_mobile_lcdc_fb", 1425cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .owner = THIS_MODULE, 14262feb075a33905c2f6ef6be484c75ba6a35f6d12dMagnus Damm .pm = &sh_mobile_lcdc_dev_pm_ops, 1427cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm }, 1428cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .probe = sh_mobile_lcdc_probe, 1429cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm .remove = sh_mobile_lcdc_remove, 1430cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm}; 1431cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1432cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic int __init sh_mobile_lcdc_init(void) 1433cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 1434cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm return platform_driver_register(&sh_mobile_lcdc_driver); 1435cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 1436cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1437cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammstatic void __exit sh_mobile_lcdc_exit(void) 1438cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm{ 1439cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm platform_driver_unregister(&sh_mobile_lcdc_driver); 1440cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm} 1441cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1442cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammmodule_init(sh_mobile_lcdc_init); 1443cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Dammmodule_exit(sh_mobile_lcdc_exit); 1444cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus Damm 1445cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus DammMODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver"); 1446cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus DammMODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); 1447cfb4f5d1750e05f43902197713c50c29e7dfbc99Magnus DammMODULE_LICENSE("GPL v2"); 1448