summaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/Kconfig33
-rw-r--r--drivers/video/fb_defio.c27
-rw-r--r--drivers/video/sh7760fb.c86
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c198
4 files changed, 232 insertions, 112 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3f3ce13fef4..d0c821992a9 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1889,10 +1889,11 @@ config FB_W100
config FB_SH_MOBILE_LCDC
tristate "SuperH Mobile LCDC framebuffer support"
depends on FB && SUPERH
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- default m
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ select FB_DEFERRED_IO
---help---
Frame buffer driver for the on-chip SH-Mobile LCD controller.
@@ -2021,17 +2022,19 @@ config FB_COBALT
depends on FB && MIPS_COBALT
config FB_SH7760
- bool "SH7760/SH7763 LCDC support"
- depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763)
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- help
- Support for the SH7760/SH7763 integrated (D)STN/TFT LCD Controller.
- Supports display resolutions up to 1024x1024 pixel, grayscale and
- color operation, with depths ranging from 1 bpp to 8 bpp monochrome
- and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
- panels <= 320 pixel horizontal resolution.
+ bool "SH7760/SH7763/SH7720/SH7721 LCDC support"
+ depends on FB && (CPU_SUBTYPE_SH7760 || CPU_SUBTYPE_SH7763 \
+ || CPU_SUBTYPE_SH7720 || CPU_SUBTYPE_SH7721)
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ ---help---
+ Support for the SH7760/SH7763/SH7720/SH7721 integrated
+ (D)STN/TFT LCD Controller.
+ Supports display resolutions up to 1024x1024 pixel, grayscale and
+ color operation, with depths ranging from 1 bpp to 8 bpp monochrome
+ and 8, 15 or 16 bpp color; 90 degrees clockwise display rotation for
+ panels <= 320 pixel horizontal resolution.
config FB_VIRTUAL
tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)"
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
index 4835bdc4e9f..082026546ae 100644
--- a/drivers/video/fb_defio.c
+++ b/drivers/video/fb_defio.c
@@ -24,6 +24,19 @@
#include <linux/rmap.h>
#include <linux/pagemap.h>
+struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs)
+{
+ void *screen_base = (void __force *) info->screen_base;
+ struct page *page;
+
+ if (is_vmalloc_addr(screen_base + offs))
+ page = vmalloc_to_page(screen_base + offs);
+ else
+ page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT);
+
+ return page;
+}
+
/* this is to find and return the vmalloc-ed fb pages */
static int fb_deferred_io_fault(struct vm_area_struct *vma,
struct vm_fault *vmf)
@@ -31,14 +44,12 @@ static int fb_deferred_io_fault(struct vm_area_struct *vma,
unsigned long offset;
struct page *page;
struct fb_info *info = vma->vm_private_data;
- /* info->screen_base is virtual memory */
- void *screen_base = (void __force *) info->screen_base;
offset = vmf->pgoff << PAGE_SHIFT;
if (offset >= info->fix.smem_len)
return VM_FAULT_SIGBUS;
- page = vmalloc_to_page(screen_base + offset);
+ page = fb_deferred_io_page(info, offset);
if (!page)
return VM_FAULT_SIGBUS;
@@ -60,6 +71,10 @@ int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync)
{
struct fb_info *info = file->private_data;
+ /* Skip if deferred io is complied-in but disabled on this fbdev */
+ if (!info->fbdefio)
+ return 0;
+
/* Kill off the delayed work */
cancel_rearming_delayed_work(&info->deferred_work);
@@ -184,7 +199,6 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_open);
void fb_deferred_io_cleanup(struct fb_info *info)
{
- void *screen_base = (void __force *) info->screen_base;
struct fb_deferred_io *fbdefio = info->fbdefio;
struct page *page;
int i;
@@ -195,9 +209,12 @@ void fb_deferred_io_cleanup(struct fb_info *info)
/* clear out the mapping that we setup */
for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
- page = vmalloc_to_page(screen_base + i);
+ page = fb_deferred_io_page(info, i);
page->mapping = NULL;
}
+
+ info->fbops->fb_mmap = NULL;
+ mutex_destroy(&fbdefio->lock);
}
EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/drivers/video/sh7760fb.c b/drivers/video/sh7760fb.c
index 8d0212da451..653bdfee305 100644
--- a/drivers/video/sh7760fb.c
+++ b/drivers/video/sh7760fb.c
@@ -13,6 +13,8 @@
*
* Thanks to Siegfried Schaefer <s.schaefer at schaefer-edv.de>
* for his original source and testing!
+ *
+ * sh7760_setcolreg get from drivers/video/sh_mobile_lcdcfb.c
*/
#include <linux/completion.h>
@@ -53,29 +55,6 @@ static irqreturn_t sh7760fb_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static void sh7760fb_wait_vsync(struct fb_info *info)
-{
- struct sh7760fb_par *par = info->par;
-
- if (par->pd->novsync)
- return;
-
- iowrite16(ioread16(par->base + LDINTR) & ~VINT_CHECK,
- par->base + LDINTR);
-
- if (par->irq < 0) {
- /* poll for vert. retrace: status bit is sticky */
- while (!(ioread16(par->base + LDINTR) & VINT_CHECK))
- cpu_relax();
- } else {
- /* a "wait_for_irq_event(par->irq)" would be extremely nice */
- init_completion(&par->vsync);
- enable_irq(par->irq);
- wait_for_completion(&par->vsync);
- disable_irq_nosync(par->irq);
- }
-}
-
/* wait_for_lps - wait until power supply has reached a certain state. */
static int wait_for_lps(struct sh7760fb_par *par, int val)
{
@@ -117,55 +96,28 @@ static int sh7760fb_blank(int blank, struct fb_info *info)
return wait_for_lps(par, lps);
}
-/* set color registers */
-static int sh7760fb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
+static int sh7760_setcolreg (u_int regno,
+ u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
{
- struct sh7760fb_par *par = info->par;
- u32 s = cmap->start;
- u32 l = cmap->len;
- u16 *r = cmap->red;
- u16 *g = cmap->green;
- u16 *b = cmap->blue;
- u32 col, tmo;
- int ret;
+ u32 *palette = info->pseudo_palette;
- ret = 0;
+ if (regno >= 16)
+ return -EINVAL;
- sh7760fb_wait_vsync(info);
+ /* only FB_VISUAL_TRUECOLOR supported */
- /* request palette access */
- iowrite16(LDPALCR_PALEN, par->base + LDPALCR);
+ red >>= 16 - info->var.red.length;
+ green >>= 16 - info->var.green.length;
+ blue >>= 16 - info->var.blue.length;
+ transp >>= 16 - info->var.transp.length;
- /* poll for access grant */
- tmo = 100;
- while (!(ioread16(par->base + LDPALCR) & LDPALCR_PALS) && (--tmo))
- cpu_relax();
+ palette[regno] = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
- if (!tmo) {
- ret = 1;
- dev_dbg(info->dev, "no palette access!\n");
- goto out;
- }
-
- while (l && (s < 256)) {
- col = ((*r) & 0xff) << 16;
- col |= ((*g) & 0xff) << 8;
- col |= ((*b) & 0xff);
- col &= SH7760FB_PALETTE_MASK;
- iowrite32(col, par->base + LDPR(s));
-
- if (s < 16)
- ((u32 *) (info->pseudo_palette))[s] = s;
-
- s++;
- l--;
- r++;
- g++;
- b++;
- }
-out:
- iowrite16(0, par->base + LDPALCR);
- return ret;
+ return 0;
}
static void encode_fix(struct fb_fix_screeninfo *fix, struct fb_info *info,
@@ -406,7 +358,7 @@ static struct fb_ops sh7760fb_ops = {
.owner = THIS_MODULE,
.fb_blank = sh7760fb_blank,
.fb_check_var = sh7760fb_check_var,
- .fb_setcmap = sh7760fb_setcmap,
+ .fb_setcolreg = sh7760_setcolreg,
.fb_set_par = sh7760fb_set_par,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index efff672fd7b..0e2b8fd24df 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -16,7 +16,9 @@
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
#include <video/sh_mobile_lcdc.h>
+#include <asm/atomic.h>
#define PALETTE_NR 16
@@ -30,11 +32,15 @@ struct sh_mobile_lcdc_chan {
u32 pseudo_palette[PALETTE_NR];
struct fb_info info;
dma_addr_t dma_handle;
+ struct fb_deferred_io defio;
};
struct sh_mobile_lcdc_priv {
void __iomem *base;
+ int irq;
#ifdef CONFIG_HAVE_CLK
+ atomic_t clk_usecnt;
+ struct clk *dot_clk;
struct clk *clk;
#endif
unsigned long lddckr;
@@ -56,7 +62,7 @@ struct sh_mobile_lcdc_priv {
/* per-channel registers */
enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
- LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
+ LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
static unsigned long lcdc_offs_mainlcd[] = {
[LDDCKPAT1R] = 0x400,
@@ -66,6 +72,7 @@ static unsigned long lcdc_offs_mainlcd[] = {
[LDMT3R] = 0x420,
[LDDFR] = 0x424,
[LDSM1R] = 0x428,
+ [LDSM2R] = 0x42c,
[LDSA1R] = 0x430,
[LDMLSR] = 0x438,
[LDHCNR] = 0x448,
@@ -83,6 +90,7 @@ static unsigned long lcdc_offs_sublcd[] = {
[LDMT3R] = 0x608,
[LDDFR] = 0x60c,
[LDSM1R] = 0x610,
+ [LDSM2R] = 0x614,
[LDSA1R] = 0x618,
[LDMLSR] = 0x620,
[LDHCNR] = 0x624,
@@ -96,6 +104,8 @@ static unsigned long lcdc_offs_sublcd[] = {
#define LCDC_RESET 0x00000100
#define DISPLAY_BEU 0x00000008
#define LCDC_ENABLE 0x00000001
+#define LDINTR_FE 0x00000400
+#define LDINTR_FS 0x00000004
static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
int reg_nr, unsigned long data)
@@ -170,6 +180,65 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
lcdc_sys_read_data,
};
+#ifdef CONFIG_HAVE_CLK
+static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
+{
+ if (atomic_inc_and_test(&priv->clk_usecnt)) {
+ clk_enable(priv->clk);
+ if (priv->dot_clk)
+ clk_enable(priv->dot_clk);
+ }
+}
+
+static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
+{
+ if (atomic_sub_return(1, &priv->clk_usecnt) == -1) {
+ if (priv->dot_clk)
+ clk_disable(priv->dot_clk);
+ clk_disable(priv->clk);
+ }
+}
+#else
+static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) {}
+static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) {}
+#endif
+
+static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ struct sh_mobile_lcdc_chan *ch = info->par;
+
+ /* enable clocks before accessing hardware */
+ sh_mobile_lcdc_clk_on(ch->lcdc);
+
+ /* trigger panel update */
+ lcdc_write_chan(ch, LDSM2R, 1);
+}
+
+static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
+{
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+ if (fbdefio)
+ schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+}
+
+static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
+{
+ struct sh_mobile_lcdc_priv *priv = data;
+ unsigned long tmp;
+
+ /* acknowledge interrupt */
+ tmp = lcdc_read(priv, _LDINTR);
+ tmp &= 0xffffff00; /* mask in high 24 bits */
+ tmp |= 0x000000ff ^ LDINTR_FS; /* status in low 8 */
+ lcdc_write(priv, _LDINTR, tmp);
+
+ /* disable clocks */
+ sh_mobile_lcdc_clk_off(priv);
+ return IRQ_HANDLED;
+}
+
static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
int start)
{
@@ -207,6 +276,11 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
int k, m;
int ret = 0;
+ /* enable clocks before accessing the hardware */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+ if (priv->ch[k].enabled)
+ sh_mobile_lcdc_clk_on(priv);
+
/* reset */
lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
@@ -249,7 +323,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
lcdc_write(priv, _LDDCKSTPR, 0);
lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
- /* interrupts are disabled */
+ /* interrupts are disabled to begin with */
lcdc_write(priv, _LDINTR, 0);
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
@@ -310,9 +384,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
return ret;
}
- /* --- display_lcdc_data() --- */
- lcdc_write(priv, _LDINTR, 0x00000f00);
-
/* word and long word swap */
lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
@@ -334,8 +405,24 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* set line size */
lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
- /* continuous read mode */
- lcdc_write_chan(ch, LDSM1R, 0);
+ /* setup deferred io if SYS bus */
+ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+ if (ch->ldmt1r_value & (1 << 12) && tmp) {
+ ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
+ ch->defio.delay = msecs_to_jiffies(tmp);
+ ch->info.fbdefio = &ch->defio;
+ fb_deferred_io_init(&ch->info);
+
+ /* one-shot mode */
+ lcdc_write_chan(ch, LDSM1R, 1);
+
+ /* enable "Frame End Interrupt Enable" bit */
+ lcdc_write(priv, _LDINTR, LDINTR_FE);
+
+ } else {
+ /* continuous read mode */
+ lcdc_write_chan(ch, LDSM1R, 0);
+ }
}
/* display output */
@@ -359,6 +446,7 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
struct sh_mobile_lcdc_board_cfg *board_cfg;
+ unsigned long tmp;
int k;
/* tell the board code to disable the panel */
@@ -367,10 +455,22 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
board_cfg = &ch->cfg.board_cfg;
if (board_cfg->display_off)
board_cfg->display_off(board_cfg->board_data);
+
+ /* cleanup deferred io if SYS bus */
+ tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+ if (ch->ldmt1r_value & (1 << 12) && tmp) {
+ fb_deferred_io_cleanup(&ch->info);
+ ch->info.fbdefio = NULL;
+ }
}
/* stop the lcdc */
sh_mobile_lcdc_start_stop(priv, 0);
+
+ /* stop clocks */
+ for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
+ if (priv->ch[k].enabled)
+ sh_mobile_lcdc_clk_off(priv);
}
static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
@@ -413,9 +513,13 @@ static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
return -EINVAL;
}
-static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
+static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
+ int clock_source,
struct sh_mobile_lcdc_priv *priv)
{
+#ifdef CONFIG_HAVE_CLK
+ char clk_name[8];
+#endif
char *str;
int icksel;
@@ -430,14 +534,21 @@ static int sh_mobile_lcdc_setup_clocks(struct device *dev, int clock_source,
priv->lddckr = icksel << 16;
#ifdef CONFIG_HAVE_CLK
+ atomic_set(&priv->clk_usecnt, -1);
+ snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
+ priv->clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(priv->clk)) {
+ dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
+ return PTR_ERR(priv->clk);
+ }
+
if (str) {
- priv->clk = clk_get(dev, str);
- if (IS_ERR(priv->clk)) {
- dev_err(dev, "cannot get clock %s\n", str);
- return PTR_ERR(priv->clk);
+ priv->dot_clk = clk_get(&pdev->dev, str);
+ if (IS_ERR(priv->dot_clk)) {
+ dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
+ clk_put(priv->clk);
+ return PTR_ERR(priv->dot_clk);
}
-
- clk_enable(priv->clk);
}
#endif
@@ -475,11 +586,34 @@ static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
.accel = FB_ACCEL_NONE,
};
+static void sh_mobile_lcdc_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ sys_fillrect(info, rect);
+ sh_mobile_lcdc_deferred_io_touch(info);
+}
+
+static void sh_mobile_lcdc_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ sys_copyarea(info, area);
+ sh_mobile_lcdc_deferred_io_touch(info);
+}
+
+static void sh_mobile_lcdc_imageblit(struct fb_info *info,
+ const struct fb_image *image)
+{
+ sys_imageblit(info, image);
+ sh_mobile_lcdc_deferred_io_touch(info);
+}
+
static struct fb_ops sh_mobile_lcdc_ops = {
.fb_setcolreg = sh_mobile_lcdc_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_fillrect = sh_mobile_lcdc_fillrect,
+ .fb_copyarea = sh_mobile_lcdc_copyarea,
+ .fb_imageblit = sh_mobile_lcdc_imageblit,
};
static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
@@ -540,8 +674,9 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "cannot find IO resource\n");
+ i = platform_get_irq(pdev, 0);
+ if (!res || i < 0) {
+ dev_err(&pdev->dev, "cannot get platform resources\n");
error = -ENOENT;
goto err0;
}
@@ -553,6 +688,14 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err0;
}
+ error = request_irq(i, sh_mobile_lcdc_irq, IRQF_DISABLED,
+ pdev->dev.bus_id, priv);
+ if (error) {
+ dev_err(&pdev->dev, "unable to request irq\n");
+ goto err1;
+ }
+
+ priv->irq = i;
platform_set_drvdata(pdev, priv);
pdata = pdev->dev.platform_data;
@@ -587,8 +730,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
goto err1;
}
- error = sh_mobile_lcdc_setup_clocks(&pdev->dev,
- pdata->clock_source, priv);
+ error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
if (error) {
dev_err(&pdev->dev, "unable to setup clocks\n");
goto err1;
@@ -637,6 +779,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
info->fix.smem_start = priv->ch[i].dma_handle;
info->screen_base = buf;
info->device = &pdev->dev;
+ info->par = &priv->ch[i];
}
if (error)
@@ -664,6 +807,10 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
(int) priv->ch[i].cfg.lcd_cfg.xres,
(int) priv->ch[i].cfg.lcd_cfg.yres,
priv->ch[i].cfg.bpp);
+
+ /* deferred io mode: disable clock to save power */
+ if (info->fbdefio)
+ sh_mobile_lcdc_clk_off(priv);
}
return 0;
@@ -697,15 +844,16 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_HAVE_CLK
- if (priv->clk) {
- clk_disable(priv->clk);
- clk_put(priv->clk);
- }
+ if (priv->dot_clk)
+ clk_put(priv->dot_clk);
+ clk_put(priv->clk);
#endif
if (priv->base)
iounmap(priv->base);
+ if (priv->irq)
+ free_irq(priv->irq, priv);
kfree(priv);
return 0;
}