Commit 395e9801 authored by Kevin Hilman's avatar Kevin Hilman

Merge branch 'davinci-upstream-accepted' into davinci-reset

parents ca3e7676 9e56b56d
/*
* Copyright (C) 2009 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef DAVINCI_KEYSCAN_H
#define DAVINCI_KEYSCAN_H
#include <linux/io.h>
enum davinci_matrix_types {
DAVINCI_KEYSCAN_MATRIX_4X4,
DAVINCI_KEYSCAN_MATRIX_5X3,
};
struct davinci_ks_platform_data {
unsigned short *keymap;
u32 keymapsize;
u8 rep:1;
u8 strobe;
u8 interval;
u8 matrix_type;
};
#endif
......@@ -423,4 +423,14 @@ config KEYBOARD_W90P910
To compile this driver as a module, choose M here: the
module will be called w90p910_keypad.
config KEYBOARD_DAVINCI
tristate "TI DaVinci Key Scan"
depends on ARCH_DAVINCI_DM365
help
Say Y to enable keypad module support for the TI DaVinci
platforms (DM365).
To compile this driver as a module, choose M here: the
module will be called davinci_keyscan.
endif
......@@ -37,3 +37,4 @@ obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
/*
* DaVinci Key Scan Driver for TI platforms
*
* Copyright (C) 2009 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
*
* Intial Code: Sandeep Paulraj <s-paulraj@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <mach/keyscan.h>
/* Key scan registers */
#define DAVINCI_KEYSCAN_KEYCTRL 0x0000
#define DAVINCI_KEYSCAN_INTENA 0x0004
#define DAVINCI_KEYSCAN_INTFLAG 0x0008
#define DAVINCI_KEYSCAN_INTCLR 0x000c
#define DAVINCI_KEYSCAN_STRBWIDTH 0x0010
#define DAVINCI_KEYSCAN_INTERVAL 0x0014
#define DAVINCI_KEYSCAN_CONTTIME 0x0018
#define DAVINCI_KEYSCAN_CURRENTST 0x001c
#define DAVINCI_KEYSCAN_PREVSTATE 0x0020
#define DAVINCI_KEYSCAN_EMUCTRL 0x0024
#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c
/* Key Control Register (KEYCTRL) */
#define DAVINCI_KEYSCAN_KEYEN 0x00000001
#define DAVINCI_KEYSCAN_PREVMODE 0x00000002
#define DAVINCI_KEYSCAN_CHATOFF 0x00000004
#define DAVINCI_KEYSCAN_AUTODET 0x00000008
#define DAVINCI_KEYSCAN_SCANMODE 0x00000010
#define DAVINCI_KEYSCAN_OUTTYPE 0x00000020
/* Masks for the interrupts */
#define DAVINCI_KEYSCAN_INT_CONT 0x00000008
#define DAVINCI_KEYSCAN_INT_OFF 0x00000004
#define DAVINCI_KEYSCAN_INT_ON 0x00000002
#define DAVINCI_KEYSCAN_INT_CHANGE 0x00000001
#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f
struct davinci_ks {
struct input_dev *input;
struct davinci_ks_platform_data *pdata;
int irq;
void __iomem *base;
resource_size_t pbase;
size_t base_size;
unsigned short keymap[];
};
/* Initializing the kp Module */
static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks)
{
struct device *dev = &davinci_ks->input->dev;
struct davinci_ks_platform_data *pdata = davinci_ks->pdata;
u32 matrix_ctrl;
/* Enable all interrupts */
__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
/* Clear interrupts if any */
__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
/* Setup the scan period = strobe + interval */
__raw_writel(pdata->strobe,
davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH);
__raw_writel(pdata->interval,
davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL);
__raw_writel(0x01,
davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME);
/* Define matrix type */
switch (pdata->matrix_type) {
case DAVINCI_KEYSCAN_MATRIX_4X4:
matrix_ctrl = 0;
break;
case DAVINCI_KEYSCAN_MATRIX_5X3:
matrix_ctrl = (1 << 6);
break;
default:
dev_err(dev->parent, "wrong matrix type\n");
return -EINVAL;
}
/* Enable key scan module and set matrix type */
__raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN |
matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL);
return 0;
}
static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id)
{
struct davinci_ks *davinci_ks = dev_id;
struct device *dev = &davinci_ks->input->dev;
unsigned short *keymap = davinci_ks->keymap;
int keymapsize = davinci_ks->pdata->keymapsize;
u32 prev_status, new_status, changed;
bool release;
int keycode = KEY_UNKNOWN;
int i;
/* Disable interrupt */
__raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
/* Reading previous and new status of the key scan */
prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE);
new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST);
changed = prev_status ^ new_status;
if (changed) {
/*
* It goes through all bits in 'changed' to ensure
* that no key changes are being missed
*/
for (i = 0 ; i < keymapsize; i++) {
if ((changed>>i) & 0x1) {
keycode = keymap[i];
release = (new_status >> i) & 0x1;
dev_dbg(dev->parent, "key %d %s\n", keycode,
release ? "released" : "pressed");
input_report_key(davinci_ks->input, keycode,
!release);
input_sync(davinci_ks->input);
}
}
/* Clearing interrupt */
__raw_writel(DAVINCI_KEYSCAN_INT_ALL,
davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);
}
/* Enable interrupts */
__raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);
return IRQ_HANDLED;
}
static int __init davinci_ks_probe(struct platform_device *pdev)
{
struct davinci_ks *davinci_ks;
struct input_dev *key_dev;
struct resource *res, *mem;
struct device *dev = &pdev->dev;
struct davinci_ks_platform_data *pdata = pdev->dev.platform_data;
int error, i;
if (!pdata->keymap) {
dev_dbg(dev, "no keymap from pdata\n");
return -EINVAL;
}
davinci_ks = kzalloc(sizeof(struct davinci_ks) +
sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL);
if (!davinci_ks) {
dev_dbg(dev, "could not allocate memory for private data\n");
return -ENOMEM;
}
memcpy(davinci_ks->keymap, pdata->keymap,
sizeof(unsigned short) * pdata->keymapsize);
key_dev = input_allocate_device();
if (!key_dev) {
dev_dbg(dev, "could not allocate input device\n");
error = -ENOMEM;
goto fail1;
}
davinci_ks->input = key_dev;
davinci_ks->irq = platform_get_irq(pdev, 0);
if (davinci_ks->irq < 0) {
dev_err(dev, "no key scan irq\n");
error = davinci_ks->irq;
goto fail2;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no mem resource\n");
error = -EINVAL;
goto fail2;
}
davinci_ks->pbase = res->start;
davinci_ks->base_size = resource_size(res);
mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size,
pdev->name);
if (!mem) {
dev_err(dev, "key scan registers at %08x are not free\n",
davinci_ks->pbase);
error = -EBUSY;
goto fail2;
}
davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size);
if (!davinci_ks->base) {
dev_err(dev, "can't ioremap MEM resource.\n");
error = -ENOMEM;
goto fail3;
}
/* Enable auto repeat feature of Linux input subsystem */
if (pdata->rep)
__set_bit(EV_REP, key_dev->evbit);
/* Setup input device */
__set_bit(EV_KEY, key_dev->evbit);
/* Setup the platform data */
davinci_ks->pdata = pdata;
for (i = 0; i < davinci_ks->pdata->keymapsize; i++)
__set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit);
key_dev->name = "davinci_keyscan";
key_dev->phys = "davinci_keyscan/input0";
key_dev->dev.parent = &pdev->dev;
key_dev->id.bustype = BUS_HOST;
key_dev->id.vendor = 0x0001;
key_dev->id.product = 0x0001;
key_dev->id.version = 0x0001;
key_dev->keycode = davinci_ks->keymap;
key_dev->keycodesize = sizeof(davinci_ks->keymap[0]);
key_dev->keycodemax = davinci_ks->pdata->keymapsize;
error = input_register_device(davinci_ks->input);
if (error < 0) {
dev_err(dev, "unable to register davinci key scan device\n");
goto fail4;
}
error = request_irq(davinci_ks->irq, davinci_ks_interrupt,
IRQF_DISABLED, pdev->name, davinci_ks);
if (error < 0) {
dev_err(dev, "unable to register davinci key scan interrupt\n");
goto fail5;
}
error = davinci_ks_initialize(davinci_ks);
if (error < 0) {
dev_err(dev, "unable to initialize davinci key scan device\n");
goto fail6;
}
platform_set_drvdata(pdev, davinci_ks);
return 0;
fail6:
free_irq(davinci_ks->irq, davinci_ks);
fail5:
input_unregister_device(davinci_ks->input);
key_dev = NULL;
fail4:
iounmap(davinci_ks->base);
fail3:
release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
fail2:
input_free_device(key_dev);
fail1:
kfree(davinci_ks);
return error;
}
static int __devexit davinci_ks_remove(struct platform_device *pdev)
{
struct davinci_ks *davinci_ks = platform_get_drvdata(pdev);
free_irq(davinci_ks->irq, davinci_ks);
input_unregister_device(davinci_ks->input);
iounmap(davinci_ks->base);
release_mem_region(davinci_ks->pbase, davinci_ks->base_size);
platform_set_drvdata(pdev, NULL);
kfree(davinci_ks);
return 0;
}
static struct platform_driver davinci_ks_driver = {
.driver = {
.name = "davinci_keyscan",
.owner = THIS_MODULE,
},
.remove = __devexit_p(davinci_ks_remove),
};
static int __init davinci_ks_init(void)
{
return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe);
}
module_init(davinci_ks_init);
static void __exit davinci_ks_exit(void)
{
platform_driver_unregister(&davinci_ks_driver);
}
module_exit(davinci_ks_exit);
MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");
MODULE_LICENSE("GPL");
......@@ -26,6 +26,7 @@ static LIST_HEAD(mtd_partitions);
struct mtd_part {
struct mtd_info mtd;
struct mtd_info *master;
struct memory_accessor macc;
uint64_t offset;
struct list_head list;
};
......@@ -327,6 +328,39 @@ int del_mtd_partitions(struct mtd_info *master)
}
EXPORT_SYMBOL(del_mtd_partitions);
/*
* This lets other kernel code access the flash data. For example, it
* might hold a board's Ethernet address, or board-specific calibration
* data generated on the manufacturing floor.
*/
static ssize_t mtd_macc_read(struct memory_accessor *macc, char *buf,
off_t offset, size_t count)
{
struct mtd_part *part = container_of(macc, struct mtd_part, macc);
ssize_t ret = -EIO;
size_t retlen;
if (part_read((struct mtd_info *)part, offset, count,
&retlen, buf) == 0)
ret = retlen;
return ret;
}
static ssize_t mtd_macc_write(struct memory_accessor *macc, const char *buf,
off_t offset, size_t count)
{
struct mtd_part *part = container_of(macc, struct mtd_part, macc);
ssize_t ret = -EIO;
size_t retlen;
if (part_write((struct mtd_info *)part, offset, count,
&retlen, buf) == 0)
ret = retlen;
return ret;
}
static struct mtd_part *add_one_partition(struct mtd_info *master,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
......@@ -364,6 +398,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.read = part_read;
slave->mtd.write = part_write;
slave->macc.read = mtd_macc_read;
slave->macc.write = mtd_macc_write;
if (master->panic_write)
slave->mtd.panic_write = part_panic_write;
......@@ -428,6 +465,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
if (part->setup)
part->setup(&slave->macc, (void *)part->context);
/* let's do some sanity checks */
if (slave->offset >= master->size) {
/* let's register it anyway to preserve ordering */
......
......@@ -599,7 +599,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
info->mask_chipsel = pdata->mask_chipsel;
/* use nandboot-capable ALE/CLE masks by default */
info->mask_ale = pdata->mask_cle ? : MASK_ALE;
info->mask_ale = pdata->mask_ale ? : MASK_ALE;
info->mask_cle = pdata->mask_cle ? : MASK_CLE;
/* Set address of hardware control function */
......
......@@ -576,6 +576,16 @@ config RTC_DRV_AB3100
comment "on-CPU RTC drivers"
config RTC_DRV_DAVINCI
tristate "TI DaVinci RTC"
depends on ARCH_DAVINCI_DM365
help
If you say yes here you get support for the RTC on the
DaVinci platforms (DM365).
This driver can also be built as a module. If so, the module
will be called rtc-davinci.
config RTC_DRV_OMAP
tristate "TI OMAP1"
depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730
......
......@@ -26,6 +26,7 @@ obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o
obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
......
/*
* DaVinci Power Management and Real Time Clock Driver for TI platforms
*
* Copyright (C) 2009 Texas Instruments, Inc
*
* Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/platform_device.h>
#include <linux/io.h>
/*
* The DaVinci RTC is a simple RTC with the following
* Sec: 0 - 59 : BCD count
* Min: 0 - 59 : BCD count
* Hour: 0 - 23 : BCD count
* Day: 0 - 0x7FFF(32767) : Binary count ( Over 89 years )
*/
/* PRTC interface registers */
#define DAVINCI_PRTCIF_PID 0x00
#define PRTCIF_CTLR 0x04
#define PRTCIF_LDATA 0x08
#define PRTCIF_UDATA 0x0C
#define PRTCIF_INTEN 0x10
#define PRTCIF_INTFLG 0x14
/* PRTCIF_CTLR bit fields */
#define PRTCIF_CTLR_BUSY BIT(31)
#define PRTCIF_CTLR_SIZE BIT(25)
#define PRTCIF_CTLR_DIR BIT(24)
#define PRTCIF_CTLR_BENU_MSB BIT(23)
#define PRTCIF_CTLR_BENU_3RD_BYTE BIT(22)
#define PRTCIF_CTLR_BENU_2ND_BYTE BIT(21)
#define PRTCIF_CTLR_BENU_LSB BIT(20)
#define PRTCIF_CTLR_BENU_MASK (0x00F00000)
#define PRTCIF_CTLR_BENL_MSB BIT(19)
#define PRTCIF_CTLR_BENL_3RD_BYTE BIT(18)
#define PRTCIF_CTLR_BENL_2ND_BYTE BIT(17)
#define PRTCIF_CTLR_BENL_LSB BIT(16)
#define PRTCIF_CTLR_BENL_MASK (0x000F0000)
/* PRTCIF_INTEN bit fields */
#define PRTCIF_INTEN_RTCSS BIT(1)
#define PRTCIF_INTEN_RTCIF BIT(0)
#define PRTCIF_INTEN_MASK (PRTCIF_INTEN_RTCSS \
| PRTCIF_INTEN_RTCIF)
/* PRTCIF_INTFLG bit fields */
#define PRTCIF_INTFLG_RTCSS BIT(1)
#define PRTCIF_INTFLG_RTCIF BIT(0)
#define PRTCIF_INTFLG_MASK (PRTCIF_INTFLG_RTCSS \
| PRTCIF_INTFLG_RTCIF)
/* PRTC subsystem registers */
#define PRTCSS_RTC_INTC_EXTENA1 (0x0C)
#define PRTCSS_RTC_CTRL (0x10)
#define PRTCSS_RTC_WDT (0x11)
#define PRTCSS_RTC_TMR0 (0x12)
#define PRTCSS_RTC_TMR1 (0x13)
#define PRTCSS_RTC_CCTRL (0x14)
#define PRTCSS_RTC_SEC (0x15)
#define PRTCSS_RTC_MIN (0x16)
#define PRTCSS_RTC_HOUR (0x17)
#define PRTCSS_RTC_DAY0 (0x18)
#define PRTCSS_RTC_DAY1 (0x19)
#define PRTCSS_RTC_AMIN (0x1A)
#define PRTCSS_RTC_AHOUR (0x1B)
#define PRTCSS_RTC_ADAY0 (0x1C)
#define PRTCSS_RTC_ADAY1 (0x1D)
#define PRTCSS_RTC_CLKC_CNT (0x20)
/* PRTCSS_RTC_INTC_EXTENA1 */
#define PRTCSS_RTC_INTC_EXTENA1_MASK (0x07)
/* PRTCSS_RTC_CTRL bit fields */
#define PRTCSS_RTC_CTRL_WDTBUS BIT(7)
#define PRTCSS_RTC_CTRL_WEN BIT(6)
#define PRTCSS_RTC_CTRL_WDRT BIT(5)
#define PRTCSS_RTC_CTRL_WDTFLG BIT(4)
#define PRTCSS_RTC_CTRL_TE BIT(3)
#define PRTCSS_RTC_CTRL_TIEN BIT(2)
#define PRTCSS_RTC_CTRL_TMRFLG BIT(1)
#define PRTCSS_RTC_CTRL_TMMD BIT(0)
/* PRTCSS_RTC_CCTRL bit fields */
#define PRTCSS_RTC_CCTRL_CALBUSY BIT(7)
#define PRTCSS_RTC_CCTRL_DAEN BIT(5)
#define PRTCSS_RTC_CCTRL_HAEN BIT(4)
#define PRTCSS_RTC_CCTRL_MAEN BIT(3)
#define PRTCSS_RTC_CCTRL_ALMFLG BIT(2)
#define PRTCSS_RTC_CCTRL_AIEN BIT(1)
#define PRTCSS_RTC_CCTRL_CAEN BIT(0)
static DEFINE_SPINLOCK(davinci_rtc_lock);
struct davinci_rtc {
struct rtc_device *rtc;
void __iomem *base;
resource_size_t pbase;
size_t base_size;
int irq;
};
static inline void rtcif_write(struct davinci_rtc *davinci_rtc,
u32 val, u32 addr)
{
writel(val, davinci_rtc->base + addr);
}
static inline u32 rtcif_read(struct davinci_rtc *davinci_rtc, u32 addr)
{
return readl(davinci_rtc->base + addr);
}
static inline void rtcif_wait(struct davinci_rtc *davinci_rtc)
{
while (rtcif_read(davinci_rtc, PRTCIF_CTLR) & PRTCIF_CTLR_BUSY)
cpu_relax();
}
static inline void rtcss_write(struct davinci_rtc *davinci_rtc,
unsigned long val, u8 addr)
{
rtcif_wait(davinci_rtc);
rtcif_write(davinci_rtc, PRTCIF_CTLR_BENL_LSB | addr, PRTCIF_CTLR);
rtcif_write(davinci_rtc, val, PRTCIF_LDATA);
rtcif_wait(davinci_rtc);
}
static inline u8 rtcss_read(struct davinci_rtc *davinci_rtc, u8 addr)
{
rtcif_wait(davinci_rtc);
rtcif_write(davinci_rtc, PRTCIF_CTLR_DIR | PRTCIF_CTLR_BENL_LSB | addr,
PRTCIF_CTLR);
rtcif_wait(davinci_rtc);
return rtcif_read(davinci_rtc, PRTCIF_LDATA);
}
static inline void davinci_rtcss_calendar_wait(struct davinci_rtc *davinci_rtc)
{
while (rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) &
PRTCSS_RTC_CCTRL_CALBUSY)
cpu_relax();
}
static irqreturn_t davinci_rtc_interrupt(int irq, void *class_dev)
{
struct davinci_rtc *davinci_rtc = class_dev;
unsigned long events = 0;
u32 irq_flg;
u8 alm_irq, tmr_irq;
u8 rtc_ctrl, rtc_cctrl;
int ret = IRQ_NONE;
irq_flg = rtcif_read(davinci_rtc, PRTCIF_INTFLG) &
PRTCIF_INTFLG_RTCSS;
alm_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) &
PRTCSS_RTC_CCTRL_ALMFLG;
tmr_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL) &
PRTCSS_RTC_CTRL_TMRFLG;
if (irq_flg) {
if (alm_irq) {
events |= RTC_IRQF | RTC_AF;
rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
rtc_cctrl |= PRTCSS_RTC_CCTRL_ALMFLG;
rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
} else if (tmr_irq) {
events |= RTC_IRQF | RTC_PF;
rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
rtc_ctrl |= PRTCSS_RTC_CTRL_TMRFLG;
rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
}
rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS,
PRTCIF_INTFLG);
rtc_update_irq(davinci_rtc->rtc, 1, events);
ret = IRQ_HANDLED;
}
return ret;
}
static int
davinci_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
u8 rtc_ctrl;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&davinci_rtc_lock, flags);
rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
switch (cmd) {
case RTC_WIE_ON:
rtc_ctrl |= PRTCSS_RTC_CTRL_WEN | PRTCSS_RTC_CTRL_WDTFLG;
break;
case RTC_WIE_OFF:
rtc_ctrl &= ~PRTCSS_RTC_CTRL_WEN;
break;
case RTC_UIE_OFF:
case RTC_UIE_ON:
ret = -ENOTTY;
break;
default:
ret = -ENOIOCTLCMD;
}
rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
return ret;
}
static int convertfromdays(u16 days, struct rtc_time *tm)
{
int tmp_days, year, mon;
for (year = 2000;; year++) {
tmp_days = rtc_year_days(1, 12, year);
if (days >= tmp_days)
days -= tmp_days;
else {
for (mon = 0;; mon++) {
tmp_days = rtc_month_days(mon, year);
if (days >= tmp_days) {
days -= tmp_days;
} else {
tm->tm_year = year - 1900;
tm->tm_mon = mon;
tm->tm_mday = days + 1;
break;
}
}
break;
}
}
return 0;
}
static int convert2days(u16 *days, struct rtc_time *tm)
{
int i;
*days = 0;
/* epoch == 1900 */
if (tm->tm_year < 100 || tm->tm_year > 199)
return -EINVAL;
for (i = 2000; i < 1900 + tm->tm_year; i++)
*days += rtc_year_days(1, 12, i);
*days += rtc_year_days(tm->tm_mday, tm->tm_mon, 1900 + tm->tm_year);
return 0;
}
static int davinci_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
u16 days = 0;
u8 day0, day1;
unsigned long flags;
spin_lock_irqsave(&davinci_rtc_lock, flags);
davinci_rtcss_calendar_wait(davinci_rtc);
tm->tm_sec = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_SEC));
davinci_rtcss_calendar_wait(davinci_rtc);
tm->tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_MIN));
davinci_rtcss_calendar_wait(davinci_rtc);
tm->tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_HOUR));
davinci_rtcss_calendar_wait(davinci_rtc);
day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY0);
davinci_rtcss_calendar_wait(davinci_rtc);
day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY1);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
days |= day1;
days <<= 8;
days |= day0;
if (convertfromdays(days, tm) < 0)
return -EINVAL;
return 0;
}
static int davinci_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
u16 days;
u8 rtc_cctrl;
unsigned long flags;
if (convert2days(&days, tm) < 0)
return -EINVAL;
spin_lock_irqsave(&davinci_rtc_lock, flags);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, bin2bcd(tm->tm_sec), PRTCSS_RTC_SEC);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, bin2bcd(tm->tm_min), PRTCSS_RTC_MIN);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, bin2bcd(tm->tm_hour), PRTCSS_RTC_HOUR);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_DAY0);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_DAY1);
rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
rtc_cctrl |= PRTCSS_RTC_CCTRL_CAEN;
rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
return 0;
}
static int davinci_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
unsigned long flags;
u8 rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
spin_lock_irqsave(&davinci_rtc_lock, flags);
if (enabled)
rtc_cctrl |= PRTCSS_RTC_CCTRL_DAEN |
PRTCSS_RTC_CCTRL_HAEN |
PRTCSS_RTC_CCTRL_MAEN |
PRTCSS_RTC_CCTRL_ALMFLG |
PRTCSS_RTC_CCTRL_AIEN;
else
rtc_cctrl &= ~PRTCSS_RTC_CCTRL_AIEN;
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
return 0;
}
static int davinci_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
u16 days = 0;
u8 day0, day1;
unsigned long flags;
spin_lock_irqsave(&davinci_rtc_lock, flags);
davinci_rtcss_calendar_wait(davinci_rtc);
alm->time.tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AMIN));
davinci_rtcss_calendar_wait(davinci_rtc);
alm->time.tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AHOUR));
davinci_rtcss_calendar_wait(davinci_rtc);
day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY0);
davinci_rtcss_calendar_wait(davinci_rtc);
day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY1);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
days |= day1;
days <<= 8;
days |= day0;
if (convertfromdays(days, &alm->time) < 0)
return -EINVAL;
alm->pending = !!(rtcss_read(davinci_rtc,
PRTCSS_RTC_CCTRL) &
PRTCSS_RTC_CCTRL_AIEN);
alm->enabled = alm->pending && device_may_wakeup(dev);
return 0;
}
static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
unsigned long flags;
u16 days;
if (alm->time.tm_mday <= 0 && alm->time.tm_mon < 0
&& alm->time.tm_year < 0) {
struct rtc_time tm;
unsigned long now, then;
davinci_rtc_read_time(dev, &tm);
rtc_tm_to_time(&tm, &now);
alm->time.tm_mday = tm.tm_mday;
alm->time.tm_mon = tm.tm_mon;
alm->time.tm_year = tm.tm_year;
rtc_tm_to_time(&alm->time, &then);
if (then < now) {
rtc_time_to_tm(now + 24 * 60 * 60, &tm);
alm->time.tm_mday = tm.tm_mday;
alm->time.tm_mon = tm.tm_mon;
alm->time.tm_year = tm.tm_year;
}
}
if (convert2days(&days, &alm->time) < 0)
return -EINVAL;
spin_lock_irqsave(&davinci_rtc_lock, flags);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_min), PRTCSS_RTC_AMIN);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_hour), PRTCSS_RTC_AHOUR);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_ADAY0);
davinci_rtcss_calendar_wait(davinci_rtc);
rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_ADAY1);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
return 0;
}
static int davinci_rtc_irq_set_state(struct device *dev, int enabled)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
unsigned long flags;
u8 rtc_ctrl;
spin_lock_irqsave(&davinci_rtc_lock, flags);
rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
if (enabled) {
while (rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL)
& PRTCSS_RTC_CTRL_WDTBUS)
cpu_relax();
rtc_ctrl |= PRTCSS_RTC_CTRL_TE;
rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
rtcss_write(davinci_rtc, 0x0, PRTCSS_RTC_CLKC_CNT);
rtc_ctrl |= PRTCSS_RTC_CTRL_TIEN |
PRTCSS_RTC_CTRL_TMMD |
PRTCSS_RTC_CTRL_TMRFLG;
} else
rtc_ctrl &= ~PRTCSS_RTC_CTRL_TIEN;
rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
return 0;
}
static int davinci_rtc_irq_set_freq(struct device *dev, int freq)
{
struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
unsigned long flags;
u16 tmr_counter = (0x8000 >> (ffs(freq) - 1));
spin_lock_irqsave(&davinci_rtc_lock, flags);
rtcss_write(davinci_rtc, tmr_counter & 0xFF, PRTCSS_RTC_TMR0);
rtcss_write(davinci_rtc, (tmr_counter & 0xFF00) >> 8, PRTCSS_RTC_TMR1);
spin_unlock_irqrestore(&davinci_rtc_lock, flags);
return 0;
}
static struct rtc_class_ops davinci_rtc_ops = {
.ioctl = davinci_rtc_ioctl,
.read_time = davinci_rtc_read_time,
.set_time = davinci_rtc_set_time,
.alarm_irq_enable = davinci_rtc_alarm_irq_enable,
.read_alarm = davinci_rtc_read_alarm,
.set_alarm = davinci_rtc_set_alarm,
.irq_set_state = davinci_rtc_irq_set_state,
.irq_set_freq = davinci_rtc_irq_set_freq,
};
static int __init davinci_rtc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct davinci_rtc *davinci_rtc;
struct resource *res, *mem;
int ret = 0;
davinci_rtc = kzalloc(sizeof(struct davinci_rtc), GFP_KERNEL);
if (!davinci_rtc) {
dev_dbg(dev, "could not allocate memory for private data\n");
return -ENOMEM;
}
davinci_rtc->irq = platform_get_irq(pdev, 0);
if (davinci_rtc->irq < 0) {
dev_err(dev, "no RTC irq\n");
ret = davinci_rtc->irq;
goto fail1;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no mem resource\n");
ret = -EINVAL;
goto fail1;
}
davinci_rtc->pbase = res->start;
davinci_rtc->base_size = resource_size(res);
mem = request_mem_region(davinci_rtc->pbase, davinci_rtc->base_size,
pdev->name);
if (!mem) {
dev_err(dev, "RTC registers at %08x are not free\n",
davinci_rtc->pbase);
ret = -EBUSY;
goto fail1;
}
davinci_rtc->base = ioremap(davinci_rtc->pbase, davinci_rtc->base_size);
if (!davinci_rtc->base) {
dev_err(dev, "unable to ioremap MEM resource\n");
ret = -ENOMEM;
goto fail2;
}
davinci_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&davinci_rtc_ops, THIS_MODULE);
if (IS_ERR(davinci_rtc->rtc)) {
dev_err(dev, "unable to register RTC device, err %ld\n",
PTR_ERR(davinci_rtc->rtc));
goto fail3;
}
rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, PRTCIF_INTFLG);
rtcif_write(davinci_rtc, 0, PRTCIF_INTEN);
rtcss_write(davinci_rtc, 0, PRTCSS_RTC_INTC_EXTENA1);
rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CTRL);
rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL);
ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt,
IRQF_DISABLED, "davinci_rtc", davinci_rtc);
if (ret < 0) {
dev_err(dev, "unable to register davinci RTC interrupt\n");
goto fail4;
}
/* Enable interrupts */
rtcif_write(davinci_rtc, PRTCIF_INTEN_RTCSS, PRTCIF_INTEN);
rtcss_write(davinci_rtc, PRTCSS_RTC_INTC_EXTENA1_MASK,
PRTCSS_RTC_INTC_EXTENA1);
rtcss_write(davinci_rtc, PRTCSS_RTC_CCTRL_CAEN, PRTCSS_RTC_CCTRL);
platform_set_drvdata(pdev, davinci_rtc);
device_init_wakeup(&pdev->dev, 0);
return 0;
fail4:
rtc_device_unregister(davinci_rtc->rtc);
fail3:
iounmap(davinci_rtc->base);
fail2:
release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size);
fail1:
kfree(davinci_rtc);
return ret;
}
static int __devexit davinci_rtc_remove(struct platform_device *pdev)
{
struct davinci_rtc *davinci_rtc = platform_get_drvdata(pdev);
device_init_wakeup(&pdev->dev, 0);
rtcif_write(davinci_rtc, 0, PRTCIF_INTEN);
free_irq(davinci_rtc->irq, davinci_rtc);
rtc_device_unregister(davinci_rtc->rtc);
iounmap(davinci_rtc->base);
release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size);
platform_set_drvdata(pdev, NULL);
kfree(davinci_rtc);
return 0;
}
static struct platform_driver davinci_rtc_driver = {
.probe = davinci_rtc_probe,
.remove = __devexit_p(davinci_rtc_remove),
.driver = {
.name = "rtc_davinci",
.owner = THIS_MODULE,
},
};
static int __init rtc_init(void)
{
return platform_driver_probe(&davinci_rtc_driver, davinci_rtc_probe);
}
module_init(rtc_init);
static void __exit rtc_exit(void)
{
platform_driver_unregister(&davinci_rtc_driver);
}
module_exit(rtc_exit);
MODULE_AUTHOR("Miguel Aguilar");
MODULE_DESCRIPTION("Texas Instruments DaVinci PRTC Driver");
MODULE_LICENSE("GPL");
......@@ -399,17 +399,12 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
new_ctrl |= OMAP_RTC_CTRL_STOP;
/* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
*
* - Boards wired so that RTC_WAKE_INT does something, and muxed
* right (W13_1610_RTC_WAKE_INT is the default after chip reset),
* should initialize the device wakeup flag appropriately.
*
* - Boards wired so RTC_ON_nOFF is used as the reset signal,
* rather than nPWRON_RESET, should forcibly enable split
* power mode. (Some chip errata report that RTC_CTRL_SPLIT
* is write-only, and always reads as zero...)
*/
device_init_wakeup(&pdev->dev, 0);
if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT)
pr_info("%s: split power mode\n", pdev->name);
......
......@@ -28,6 +28,8 @@
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/console.h>
#include <video/da8xx-fb.h>
#define DRIVER_NAME "da8xx_lcdc"
......@@ -113,6 +115,12 @@ struct da8xx_fb_par {
unsigned short pseudo_palette[16];
unsigned int databuf_sz;
unsigned int palette_sz;
unsigned int pxl_clk;
int blank;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
void (*panel_power_ctrl)(int);
};
/* Variable Screen Information */
......@@ -155,7 +163,7 @@ struct da8xx_panel {
int vfp; /* Vertical front porch */
int vbp; /* Vertical back porch */
int vsw; /* Vertical Sync Pulse Width */
int pxl_clk; /* Pixel clock */
unsigned int pxl_clk; /* Pixel clock */
unsigned char invert_pxl_clk; /* Invert Pixel clock */
};
......@@ -171,7 +179,7 @@ static struct da8xx_panel known_lcd_panels[] = {
.vfp = 2,
.vbp = 2,
.vsw = 0,
.pxl_clk = 0x10,
.pxl_clk = 4608000,
.invert_pxl_clk = 1,
},
/* Sharp LK043T1DG01 */
......@@ -185,13 +193,23 @@ static struct da8xx_panel known_lcd_panels[] = {
.vfp = 2,
.vbp = 2,
.vsw = 10,
.pxl_clk = 0x12,
.pxl_clk = 7833600,
.invert_pxl_clk = 0,
},
};
/* Enable the Raster Engine of the LCD Controller */
static inline void lcd_enable_raster(void)
{
u32 reg;
reg = lcdc_read(LCD_RASTER_CTRL_REG);
if (!(reg & LCD_RASTER_ENABLE))
lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
}
/* Disable the Raster Engine of the LCD Controller */
static void lcd_disable_raster(struct da8xx_fb_par *par)
static inline void lcd_disable_raster(void)
{
u32 reg;
......@@ -443,25 +461,36 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green,
static void lcd_reset(struct da8xx_fb_par *par)
{
/* Disable the Raster if previously Enabled */
if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE)
lcd_disable_raster(par);
lcd_disable_raster();
/* DMA has to be disabled */
lcdc_write(0, LCD_DMA_CTRL_REG);
lcdc_write(0, LCD_RASTER_CTRL_REG);
}
static void lcd_calc_clk_divider(struct da8xx_fb_par *par)
{
unsigned int lcd_clk, div;
lcd_clk = clk_get_rate(par->lcdc_clk);
div = lcd_clk / par->pxl_clk;
/* Configure the LCD clock divisor. */
lcdc_write(LCD_CLK_DIVISOR(div) |
(LCD_RASTER_MODE & 0x1), LCD_CTRL_REG);
}
static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
struct da8xx_panel *panel)
{
u32 bpp;
int ret = 0;
unsigned int lcd_clk, div;
lcd_reset(par);
/* Configure the LCD clock divisor. */
lcdc_write(LCD_CLK_DIVISOR(panel->pxl_clk) |
(LCD_RASTER_MODE & 0x1), LCD_CTRL_REG);
/* Calculate the divider */
lcd_calc_clk_divider(par);
if (panel->invert_pxl_clk)
lcdc_write((lcdc_read(LCD_RASTER_TIMING_2_REG) |
......@@ -513,13 +542,11 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg,
static irqreturn_t lcdc_irq_handler(int irq, void *arg)
{
u32 stat = lcdc_read(LCD_STAT_REG);
u32 reg;
if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) {
reg = lcdc_read(LCD_RASTER_CTRL_REG);
lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
lcd_disable_raster();
lcdc_write(stat, LCD_STAT_REG);
lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
lcd_enable_raster();
} else
lcdc_write(stat, LCD_STAT_REG);
......@@ -574,6 +601,38 @@ static int fb_check_var(struct fb_var_screeninfo *var,
return err;
}
#ifdef CONFIG_CPU_FREQ
static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb,
unsigned long val, void *data)
{
struct da8xx_fb_par *par;
par = container_of(nb, struct da8xx_fb_par, freq_transition);
if (val == CPUFREQ_PRECHANGE) {
lcd_disable_raster();
} else if (val == CPUFREQ_POSTCHANGE) {
lcd_calc_clk_divider(par);
lcd_enable_raster();
}
return 0;
}
static inline int lcd_da8xx_cpufreq_register(struct da8xx_fb_par *par)
{
par->freq_transition.notifier_call = lcd_da8xx_cpufreq_transition;
return cpufreq_register_notifier(&par->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
static inline void lcd_da8xx_cpufreq_deregister(struct da8xx_fb_par *par)
{
cpufreq_unregister_notifier(&par->freq_transition,
CPUFREQ_TRANSITION_NOTIFIER);
}
#endif
static int __devexit fb_remove(struct platform_device *dev)
{
struct fb_info *info = dev_get_drvdata(&dev->dev);
......@@ -581,8 +640,13 @@ static int __devexit fb_remove(struct platform_device *dev)
if (info) {
struct da8xx_fb_par *par = info->par;
if (lcdc_read(LCD_RASTER_CTRL_REG) & LCD_RASTER_ENABLE)
lcd_disable_raster(par);
#ifdef CONFIG_CPU_FREQ
lcd_da8xx_cpufreq_deregister(par);
#endif
if (par->panel_power_ctrl)
par->panel_power_ctrl(0);
lcd_disable_raster();
lcdc_write(0, LCD_RASTER_CTRL_REG);
/* disable DMA */
......@@ -639,6 +703,35 @@ static int fb_ioctl(struct fb_info *info, unsigned int cmd,
return 0;
}
static int cfb_blank(int blank, struct fb_info *info)
{
struct da8xx_fb_par *par = info->par;
int ret = 0;
if (par->blank == blank)
return 0;
par->blank = blank;
switch (blank) {
case FB_BLANK_UNBLANK:
if (par->panel_power_ctrl)
par->panel_power_ctrl(1);
lcd_enable_raster();
break;
case FB_BLANK_POWERDOWN:
if (par->panel_power_ctrl)
par->panel_power_ctrl(0);
lcd_disable_raster();
break;
default:
ret = -EINVAL;
}
return ret;
}
static struct fb_ops da8xx_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = fb_check_var,
......@@ -647,6 +740,7 @@ static struct fb_ops da8xx_fb_ops = {
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_blank = cfb_blank,
};
static int __init fb_probe(struct platform_device *device)
......@@ -721,6 +815,12 @@ static int __init fb_probe(struct platform_device *device)
}
par = da8xx_fb_info->par;
par->lcdc_clk = fb_clk;
par->pxl_clk = lcdc_info->pxl_clk;
if (fb_pdata->panel_power_ctrl) {
par->panel_power_ctrl = fb_pdata->panel_power_ctrl;
par->panel_power_ctrl(1);
}
if (lcd_init(par, lcd_cfg, lcdc_info) < 0) {
dev_err(&device->dev, "lcd_init failed\n");
......@@ -754,8 +854,6 @@ static int __init fb_probe(struct platform_device *device)
da8xx_fb_fix.smem_len = par->databuf_sz - par->palette_sz;
da8xx_fb_fix.line_length = (lcdc_info->width * lcd_cfg->bpp) / 8;
par->lcdc_clk = fb_clk;
par->irq = platform_get_irq(device, 0);
if (par->irq < 0) {
ret = -ENOENT;
......@@ -814,12 +912,24 @@ static int __init fb_probe(struct platform_device *device)
goto err_dealloc_cmap;
}
#ifdef CONFIG_CPU_FREQ
ret = lcd_da8xx_cpufreq_register(par);
if (ret) {
dev_err(&device->dev, "failed to register cpufreq\n");
goto err_cpu_freq;
}
#endif
/* enable raster engine */
lcdc_write(lcdc_read(LCD_RASTER_CTRL_REG) |
LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG);
lcd_enable_raster();
return 0;
#ifdef CONFIG_CPU_FREQ
err_cpu_freq:
unregister_framebuffer(da8xx_fb_info);
#endif
err_dealloc_cmap:
fb_dealloc_cmap(&da8xx_fb_info->cmap);
......@@ -852,11 +962,35 @@ err_request_mem:
#ifdef CONFIG_PM
static int fb_suspend(struct platform_device *dev, pm_message_t state)
{
return -EBUSY;
struct fb_info *info = platform_get_drvdata(dev);
struct da8xx_fb_par *par = info->par;
acquire_console_sem();
if (par->panel_power_ctrl)
par->panel_power_ctrl(0);
fb_set_suspend(info, 1);
lcd_disable_raster();
clk_disable(par->lcdc_clk);
release_console_sem();
return 0;
}
static int fb_resume(struct platform_device *dev)
{
return -EBUSY;
struct fb_info *info = platform_get_drvdata(dev);
struct da8xx_fb_par *par = info->par;
acquire_console_sem();
if (par->panel_power_ctrl)
par->panel_power_ctrl(1);
clk_enable(par->lcdc_clk);
lcd_enable_raster();
fb_set_suspend(info, 0);
release_console_sem();
return 0;
}
#else
#define fb_suspend NULL
......
......@@ -10,6 +10,7 @@
#define MTD_PARTITIONS_H
#include <linux/types.h>
#include <linux/memory.h>
/*
......@@ -40,6 +41,8 @@ struct mtd_partition {
uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/
void (*setup)(struct memory_accessor *, void *context);
void *context;
};
#define MTDPART_OFS_NXTBLK (-2)
......
......@@ -38,6 +38,7 @@ struct da8xx_lcdc_platform_data {
const char manu_name[10];
void *controller_data;
const char type[25];
void (*panel_power_ctrl)(int);
};
struct lcd_ctrl_config {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment