Merge branch 'akpm' (incoming from Andrew)

Merge misc patches from Andrew Morton:

 - Florian has vanished so I appear to have become fbdev maintainer
   again :(

 - Joel and Mark are distracted to welcome to the new OCFS2 maintainer

 - The backlight queue

 - Small core kernel changes

 - lib/ updates

 - The rtc queue

 - Various random bits

* akpm: (164 commits)
  rtc: rtc-davinci: use devm_*() functions
  rtc: rtc-max8997: use devm_request_threaded_irq()
  rtc: rtc-max8907: use devm_request_threaded_irq()
  rtc: rtc-da9052: use devm_request_threaded_irq()
  rtc: rtc-wm831x: use devm_request_threaded_irq()
  rtc: rtc-tps80031: use devm_request_threaded_irq()
  rtc: rtc-lp8788: use devm_request_threaded_irq()
  rtc: rtc-coh901331: use devm_clk_get()
  rtc: rtc-vt8500: use devm_*() functions
  rtc: rtc-tps6586x: use devm_request_threaded_irq()
  rtc: rtc-imxdi: use devm_clk_get()
  rtc: rtc-cmos: use dev_warn()/dev_dbg() instead of printk()/pr_debug()
  rtc: rtc-pcf8583: use dev_warn() instead of printk()
  rtc: rtc-sun4v: use pr_warn() instead of printk()
  rtc: rtc-vr41xx: use dev_info() instead of printk()
  rtc: rtc-rs5c313: use pr_err() instead of printk()
  rtc: rtc-at91rm9200: use dev_dbg()/dev_err() instead of printk()/pr_debug()
  rtc: rtc-rs5c372: use dev_dbg()/dev_warn() instead of printk()/pr_debug()
  rtc: rtc-ds2404: use dev_err() instead of printk()
  rtc: rtc-efi: use dev_err()/dev_warn()/pr_err() instead of printk()
  ...
This commit is contained in:
Linus Torvalds 2013-02-21 17:38:49 -08:00
commit 7c2db36e73
165 changed files with 9681 additions and 1307 deletions

View file

@ -48,3 +48,8 @@ max_ratio (read-write)
most of the write-back cache. For example in case of an NFS most of the write-back cache. For example in case of an NFS
mount that is prone to get stuck, or a FUSE mount which cannot mount that is prone to get stuck, or a FUSE mount which cannot
be trusted to play fair. be trusted to play fair.
stable_pages_required (read-only)
If set, the backing device requires that all pages comprising a write
request must not be changed until writeout is complete.

View file

@ -4,7 +4,7 @@ Kernel driver lp855x
Backlight driver for LP855x ICs Backlight driver for LP855x ICs
Supported chips: Supported chips:
Texas Instruments LP8550, LP8551, LP8552, LP8553 and LP8556 Texas Instruments LP8550, LP8551, LP8552, LP8553, LP8556 and LP8557
Author: Milo(Woogyom) Kim <milo.kim@ti.com> Author: Milo(Woogyom) Kim <milo.kim@ti.com>
@ -24,7 +24,7 @@ Value : pwm based or register based
2) chip_id 2) chip_id
The lp855x chip id. The lp855x chip id.
Value : lp8550/lp8551/lp8552/lp8553/lp8556 Value : lp8550/lp8551/lp8552/lp8553/lp8556/lp8557
Platform data for lp855x Platform data for lp855x
------------------------ ------------------------

View file

@ -1228,7 +1228,7 @@ hierarchy and routing of interrupts in the hardware.
The interrupt tree model is fully described in the The interrupt tree model is fully described in the
document "Open Firmware Recommended Practice: Interrupt document "Open Firmware Recommended Practice: Interrupt
Mapping Version 0.9". The document is available at: Mapping Version 0.9". The document is available at:
<http://playground.sun.com/1275/practice>. <http://www.openfirmware.org/ofwg/practice/>
1) interrupts property 1) interrupts property
---------------------- ----------------------

View file

@ -53,6 +53,14 @@ Struct Resources:
For printing struct resources. The 'R' and 'r' specifiers result in a For printing struct resources. The 'R' and 'r' specifiers result in a
printed resource with ('R') or without ('r') a decoded flags member. printed resource with ('R') or without ('r') a decoded flags member.
Physical addresses:
%pa 0x01234567 or 0x0123456789abcdef
For printing a phys_addr_t type (and its derivatives, such as
resource_size_t) which can vary based on build options, regardless of
the width of the CPU data path. Passed by reference.
Raw buffer as a hex string: Raw buffer as a hex string:
%*ph 00 01 02 ... 3f %*ph 00 01 02 ... 3f
%*phC 00:01:02: ... :3f %*phC 00:01:02: ... :3f
@ -150,9 +158,9 @@ s64 SHOULD be printed with %lld/%llx, (long long):
printk("%lld", (long long)s64_var); printk("%lld", (long long)s64_var);
If <type> is dependent on a config option for its size (e.g., sector_t, If <type> is dependent on a config option for its size (e.g., sector_t,
blkcnt_t, phys_addr_t, resource_size_t) or is architecture-dependent blkcnt_t) or is architecture-dependent for its size (e.g., tcflag_t), use a
for its size (e.g., tcflag_t), use a format specifier of its largest format specifier of its largest possible type and explicitly cast to it.
possible type and explicitly cast to it. Example: Example:
printk("test: sector number/total blocks: %llu/%llu\n", printk("test: sector number/total blocks: %llu/%llu\n",
(unsigned long long)sector, (unsigned long long)blockcount); (unsigned long long)sector, (unsigned long long)blockcount);

View file

@ -1956,7 +1956,8 @@ F: drivers/misc/*
CHECKPATCH CHECKPATCH
M: Andy Whitcroft <apw@canonical.com> M: Andy Whitcroft <apw@canonical.com>
S: Supported M: Joe Perches <joe@perches.com>
S: Maintained
F: scripts/checkpatch.pl F: scripts/checkpatch.pl
CHINESE DOCUMENTATION CHINESE DOCUMENTATION
@ -5035,6 +5036,10 @@ L: linux-mm@kvack.org
W: http://www.linux-mm.org W: http://www.linux-mm.org
S: Maintained S: Maintained
F: include/linux/mm.h F: include/linux/mm.h
F: include/linux/gfp.h
F: include/linux/mmzone.h
F: include/linux/memory_hotplug.h
F: include/linux/vmalloc.h
F: mm/ F: mm/
MEMORY RESOURCE CONTROLLER MEMORY RESOURCE CONTROLLER

View file

@ -1300,17 +1300,15 @@ static unsigned long
arch_get_unmapped_area_1(unsigned long addr, unsigned long len, arch_get_unmapped_area_1(unsigned long addr, unsigned long len,
unsigned long limit) unsigned long limit)
{ {
struct vm_area_struct *vma = find_vma(current->mm, addr); struct vm_unmapped_area_info info;
while (1) { info.flags = 0;
/* At this point: (!vma || addr < vma->vm_end). */ info.length = len;
if (limit - len < addr) info.low_limit = addr;
return -ENOMEM; info.high_limit = limit;
if (!vma || addr + len <= vma->vm_start) info.align_mask = 0;
return addr; info.align_offset = 0;
addr = vma->vm_end; return vm_unmapped_area(&info);
vma = vma->vm_next;
}
} }
unsigned long unsigned long

View file

@ -131,6 +131,12 @@
clocks = <&coreclk 0>; clocks = <&coreclk 0>;
status = "disabled"; status = "disabled";
}; };
rtc@10300 {
compatible = "marvell,orion-rtc";
reg = <0xd0010300 0x20>;
interrupts = <50>;
};
}; };
}; };

View file

@ -42,6 +42,14 @@ CONFIG_SMC91X=y
# CONFIG_SERIO is not set # CONFIG_SERIO is not set
CONFIG_SERIAL_PXA=y CONFIG_SERIAL_PXA=y
CONFIG_SERIAL_PXA_CONSOLE=y CONFIG_SERIAL_PXA_CONSOLE=y
CONFIG_SPI=y
CONFIG_FB=y
CONFIG_MMP_DISP=y
CONFIG_MMP_DISP_CONTROLLER=y
CONFIG_MMP_SPI=y
CONFIG_MMP_PANEL_TPOHVGA=y
CONFIG_MMP_FB=y
CONFIG_LOGO=y
# CONFIG_LEGACY_PTYS is not set # CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set # CONFIG_HW_RANDOM is not set
# CONFIG_HWMON is not set # CONFIG_HWMON is not set

View file

@ -8,6 +8,7 @@ extern void __init pxa910_init_irq(void);
#include <linux/i2c/pxa-i2c.h> #include <linux/i2c/pxa-i2c.h>
#include <mach/devices.h> #include <mach/devices.h>
#include <linux/platform_data/mtd-nand-pxa3xx.h> #include <linux/platform_data/mtd-nand-pxa3xx.h>
#include <video/mmp_disp.h>
extern struct pxa_device_desc pxa910_device_uart1; extern struct pxa_device_desc pxa910_device_uart1;
extern struct pxa_device_desc pxa910_device_uart2; extern struct pxa_device_desc pxa910_device_uart2;
@ -21,7 +22,9 @@ extern struct pxa_device_desc pxa910_device_nand;
extern struct platform_device pxa168_device_u2o; extern struct platform_device pxa168_device_u2o;
extern struct platform_device pxa168_device_u2ootg; extern struct platform_device pxa168_device_u2ootg;
extern struct platform_device pxa168_device_u2oehci; extern struct platform_device pxa168_device_u2oehci;
extern struct pxa_device_desc pxa910_device_disp;
extern struct pxa_device_desc pxa910_device_fb;
extern struct pxa_device_desc pxa910_device_panel;
extern struct platform_device pxa910_device_gpio; extern struct platform_device pxa910_device_gpio;
extern struct platform_device pxa910_device_rtc; extern struct platform_device pxa910_device_rtc;

View file

@ -134,6 +134,9 @@ PXA910_DEVICE(pwm2, "pxa910-pwm", 1, NONE, 0xd401a400, 0x10);
PXA910_DEVICE(pwm3, "pxa910-pwm", 2, NONE, 0xd401a800, 0x10); PXA910_DEVICE(pwm3, "pxa910-pwm", 2, NONE, 0xd401a800, 0x10);
PXA910_DEVICE(pwm4, "pxa910-pwm", 3, NONE, 0xd401ac00, 0x10); PXA910_DEVICE(pwm4, "pxa910-pwm", 3, NONE, 0xd401ac00, 0x10);
PXA910_DEVICE(nand, "pxa3xx-nand", -1, NAND, 0xd4283000, 0x80, 97, 99); PXA910_DEVICE(nand, "pxa3xx-nand", -1, NAND, 0xd4283000, 0x80, 97, 99);
PXA910_DEVICE(disp, "mmp-disp", 0, LCD, 0xd420b000, 0x1ec);
PXA910_DEVICE(fb, "mmp-fb", -1, NONE, 0, 0);
PXA910_DEVICE(panel, "tpo-hvga", -1, NONE, 0, 0);
struct resource pxa910_resource_gpio[] = { struct resource pxa910_resource_gpio[] = {
{ {

View file

@ -19,6 +19,8 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/mfd/88pm860x.h> #include <linux/mfd/88pm860x.h>
#include <linux/platform_data/mv_usb.h> #include <linux/platform_data/mv_usb.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
@ -184,6 +186,92 @@ static struct pxa3xx_nand_platform_data dkb_nand_info = {
}; };
#endif #endif
#ifdef CONFIG_MMP_DISP
/* path config */
#define CFG_IOPADMODE(iopad) (iopad) /* 0x0 ~ 0xd */
#define SCLK_SOURCE_SELECT(x) (x << 30) /* 0x0 ~ 0x3 */
/* link config */
#define CFG_DUMBMODE(mode) (mode << 28) /* 0x0 ~ 0x6*/
#define CFG_GRA_SWAPRB(x) (x << 0) /* 1: rbswap enabled */
static struct mmp_mach_path_config dkb_disp_config[] = {
[0] = {
.name = "mmp-parallel",
.overlay_num = 2,
.output_type = PATH_OUT_PARALLEL,
.path_config = CFG_IOPADMODE(0x1)
| SCLK_SOURCE_SELECT(0x1),
.link_config = CFG_DUMBMODE(0x2)
| CFG_GRA_SWAPRB(0x1),
},
};
static struct mmp_mach_plat_info dkb_disp_info = {
.name = "mmp-disp",
.clk_name = "disp0",
.path_num = 1,
.paths = dkb_disp_config,
};
static struct mmp_buffer_driver_mach_info dkb_fb_info = {
.name = "mmp-fb",
.path_name = "mmp-parallel",
.overlay_id = 0,
.dmafetch_id = 1,
.default_pixfmt = PIXFMT_RGB565,
};
static void dkb_tpo_panel_power(int on)
{
int err;
u32 spi_reset = mfp_to_gpio(MFP_PIN_GPIO106);
if (on) {
err = gpio_request(spi_reset, "TPO_LCD_SPI_RESET");
if (err) {
pr_err("failed to request GPIO for TPO LCD RESET\n");
return;
}
gpio_direction_output(spi_reset, 0);
udelay(100);
gpio_set_value(spi_reset, 1);
gpio_free(spi_reset);
} else {
err = gpio_request(spi_reset, "TPO_LCD_SPI_RESET");
if (err) {
pr_err("failed to request LCD RESET gpio\n");
return;
}
gpio_set_value(spi_reset, 0);
gpio_free(spi_reset);
}
}
static struct mmp_mach_panel_info dkb_tpo_panel_info = {
.name = "tpo-hvga",
.plat_path_name = "mmp-parallel",
.plat_set_onoff = dkb_tpo_panel_power,
};
static struct spi_board_info spi_board_info[] __initdata = {
{
.modalias = "tpo-hvga",
.platform_data = &dkb_tpo_panel_info,
.bus_num = 5,
}
};
static void __init add_disp(void)
{
pxa_register_device(&pxa910_device_disp,
&dkb_disp_info, sizeof(dkb_disp_info));
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
pxa_register_device(&pxa910_device_fb,
&dkb_fb_info, sizeof(dkb_fb_info));
pxa_register_device(&pxa910_device_panel,
&dkb_tpo_panel_info, sizeof(dkb_tpo_panel_info));
}
#endif
static void __init ttc_dkb_init(void) static void __init ttc_dkb_init(void)
{ {
mfp_config(ARRAY_AND_SIZE(ttc_dkb_pin_config)); mfp_config(ARRAY_AND_SIZE(ttc_dkb_pin_config));
@ -212,6 +300,10 @@ static void __init ttc_dkb_init(void)
pxa168_device_u2ootg.dev.platform_data = &ttc_usb_pdata; pxa168_device_u2ootg.dev.platform_data = &ttc_usb_pdata;
platform_device_register(&pxa168_device_u2ootg); platform_device_register(&pxa168_device_u2ootg);
#endif #endif
#ifdef CONFIG_MMP_DISP
add_disp();
#endif
} }
MACHINE_START(TTC_DKB, "PXA910-based TTC_DKB Development Platform") MACHINE_START(TTC_DKB, "PXA910-based TTC_DKB Development Platform")

View file

@ -22,11 +22,6 @@ typedef unsigned short __kernel_uid_t;
typedef unsigned short __kernel_gid_t; typedef unsigned short __kernel_gid_t;
#define __kernel_uid_t __kernel_uid_t #define __kernel_uid_t __kernel_uid_t
typedef __SIZE_TYPE__ __kernel_size_t;
typedef long __kernel_ssize_t;
typedef int __kernel_ptrdiff_t;
#define __kernel_size_t __kernel_size_t
typedef unsigned short __kernel_old_dev_t; typedef unsigned short __kernel_old_dev_t;
#define __kernel_old_dev_t __kernel_old_dev_t #define __kernel_old_dev_t __kernel_old_dev_t

View file

@ -29,7 +29,7 @@ void __init pcibios_fixup_irqs(void)
struct pci_dev *dev = NULL; struct pci_dev *dev = NULL;
u8 line, pin; u8 line, pin;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { for_each_pci_dev(dev) {
pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
if (pin) { if (pin) {
dev->irq = XIRQ1; dev->irq = XIRQ1;

View file

@ -413,12 +413,6 @@ config TILE_USB
Provides USB host adapter support for the built-in EHCI and OHCI Provides USB host adapter support for the built-in EHCI and OHCI
interfaces on TILE-Gx chips. interfaces on TILE-Gx chips.
# USB OHCI needs the bounce pool since tilegx will often have more
# than 4GB of memory, but we don't currently use the IOTLB to present
# a 32-bit address to OHCI. So we need to use a bounce pool instead.
config NEED_BOUNCE_POOL
def_bool USB_OHCI_HCD
source "drivers/pci/hotplug/Kconfig" source "drivers/pci/hotplug/Kconfig"
endmenu endmenu

View file

@ -1474,6 +1474,11 @@ void blk_queue_bio(struct request_queue *q, struct bio *bio)
*/ */
blk_queue_bounce(q, &bio); blk_queue_bounce(q, &bio);
if (bio_integrity_enabled(bio) && bio_integrity_prep(bio)) {
bio_endio(bio, -EIO);
return;
}
if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) { if (bio->bi_rw & (REQ_FLUSH | REQ_FUA)) {
spin_lock_irq(q->queue_lock); spin_lock_irq(q->queue_lock);
where = ELEVATOR_INSERT_FLUSH; where = ELEVATOR_INSERT_FLUSH;
@ -1714,9 +1719,6 @@ generic_make_request_checks(struct bio *bio)
*/ */
blk_partition_remap(bio); blk_partition_remap(bio);
if (bio_integrity_enabled(bio) && bio_integrity_prep(bio))
goto end_io;
if (bio_check_eod(bio, nr_sectors)) if (bio_check_eod(bio, nr_sectors))
goto end_io; goto end_io;

View file

@ -420,6 +420,8 @@ int blk_integrity_register(struct gendisk *disk, struct blk_integrity *template)
} else } else
bi->name = bi_unsupported_name; bi->name = bi_unsupported_name;
disk->queue->backing_dev_info.capabilities |= BDI_CAP_STABLE_WRITES;
return 0; return 0;
} }
EXPORT_SYMBOL(blk_integrity_register); EXPORT_SYMBOL(blk_integrity_register);
@ -438,6 +440,8 @@ void blk_integrity_unregister(struct gendisk *disk)
if (!disk || !disk->integrity) if (!disk || !disk->integrity)
return; return;
disk->queue->backing_dev_info.capabilities &= ~BDI_CAP_STABLE_WRITES;
bi = disk->integrity; bi = disk->integrity;
kobject_uevent(&bi->kobj, KOBJ_REMOVE); kobject_uevent(&bi->kobj, KOBJ_REMOVE);

View file

@ -61,7 +61,7 @@ config SUNGEM
select SUNGEM_PHY select SUNGEM_PHY
---help--- ---help---
Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also Support for the Sun GEM chip, aka Sun GigabitEthernet/P 2.0. See also
<http://www.sun.com/products-n-solutions/hardware/docs/pdf/806-3985-10.pdf>. <http://docs.oracle.com/cd/E19455-01/806-3985-10/806-3985-10.pdf>.
config CASSINI config CASSINI
tristate "Sun Cassini support" tristate "Sun Cassini support"
@ -69,7 +69,7 @@ config CASSINI
select CRC32 select CRC32
---help--- ---help---
Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also Support for the Sun Cassini chip, aka Sun GigaSwift Ethernet. See also
<http://www.sun.com/products-n-solutions/hardware/docs/pdf/817-4341-10.pdf> <http://docs.oracle.com/cd/E19113-01/giga.ether.pci/817-4341-10/817-4341-10.pdf>.
config SUNVNET config SUNVNET
tristate "Sun Virtual Network support" tristate "Sun Virtual Network support"

View file

@ -484,7 +484,7 @@ static int socket_early_resume(struct pcmcia_socket *skt)
static int socket_late_resume(struct pcmcia_socket *skt) static int socket_late_resume(struct pcmcia_socket *skt)
{ {
int ret; int ret = 0;
mutex_lock(&skt->ops_mutex); mutex_lock(&skt->ops_mutex);
skt->state &= ~SOCKET_SUSPEND; skt->state &= ~SOCKET_SUSPEND;
@ -511,19 +511,31 @@ static int socket_late_resume(struct pcmcia_socket *skt)
return socket_insert(skt); return socket_insert(skt);
} }
if (!(skt->state & SOCKET_CARDBUS) && (skt->callback))
ret = skt->callback->early_resume(skt);
return ret;
}
/*
* Finalize the resume. In case of a cardbus socket, we have
* to rebind the devices as we can't be certain that it has been
* replaced, or not.
*/
static int socket_complete_resume(struct pcmcia_socket *skt)
{
int ret = 0;
#ifdef CONFIG_CARDBUS #ifdef CONFIG_CARDBUS
if (skt->state & SOCKET_CARDBUS) { if (skt->state & SOCKET_CARDBUS) {
/* We can't be sure the CardBus card is the same /* We can't be sure the CardBus card is the same
* as the one previously inserted. Therefore, remove * as the one previously inserted. Therefore, remove
* and re-add... */ * and re-add... */
cb_free(skt); cb_free(skt);
cb_alloc(skt); ret = cb_alloc(skt);
return 0; if (ret)
cb_free(skt);
} }
#endif #endif
if (!(skt->state & SOCKET_CARDBUS) && (skt->callback)) return ret;
skt->callback->early_resume(skt);
return 0;
} }
/* /*
@ -533,11 +545,15 @@ static int socket_late_resume(struct pcmcia_socket *skt)
*/ */
static int socket_resume(struct pcmcia_socket *skt) static int socket_resume(struct pcmcia_socket *skt)
{ {
int err;
if (!(skt->state & SOCKET_SUSPEND)) if (!(skt->state & SOCKET_SUSPEND))
return -EBUSY; return -EBUSY;
socket_early_resume(skt); socket_early_resume(skt);
return socket_late_resume(skt); err = socket_late_resume(skt);
if (!err)
err = socket_complete_resume(skt);
return err;
} }
static void socket_remove(struct pcmcia_socket *skt) static void socket_remove(struct pcmcia_socket *skt)
@ -848,6 +864,12 @@ static int __used pcmcia_socket_dev_resume(struct device *dev)
return __pcmcia_pm_op(dev, socket_late_resume); return __pcmcia_pm_op(dev, socket_late_resume);
} }
static void __used pcmcia_socket_dev_complete(struct device *dev)
{
WARN(__pcmcia_pm_op(dev, socket_complete_resume),
"failed to complete resume");
}
static const struct dev_pm_ops pcmcia_socket_pm_ops = { static const struct dev_pm_ops pcmcia_socket_pm_ops = {
/* dev_resume may be called with IRQs enabled */ /* dev_resume may be called with IRQs enabled */
SET_SYSTEM_SLEEP_PM_OPS(NULL, SET_SYSTEM_SLEEP_PM_OPS(NULL,
@ -862,6 +884,7 @@ static const struct dev_pm_ops pcmcia_socket_pm_ops = {
.resume_noirq = pcmcia_socket_dev_resume_noirq, .resume_noirq = pcmcia_socket_dev_resume_noirq,
.thaw_noirq = pcmcia_socket_dev_resume_noirq, .thaw_noirq = pcmcia_socket_dev_resume_noirq,
.restore_noirq = pcmcia_socket_dev_resume_noirq, .restore_noirq = pcmcia_socket_dev_resume_noirq,
.complete = pcmcia_socket_dev_complete,
}; };
#define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops) #define PCMCIA_SOCKET_CLASS_PM_OPS (&pcmcia_socket_pm_ops)

View file

@ -204,6 +204,12 @@ config RTC_DRV_DS3232
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called rtc-ds3232. will be called rtc-ds3232.
config RTC_DRV_LP8788
tristate "TI LP8788 RTC driver"
depends on MFD_LP8788
help
Say Y to enable support for the LP8788 RTC/ALARM driver.
config RTC_DRV_MAX6900 config RTC_DRV_MAX6900
tristate "Maxim MAX6900" tristate "Maxim MAX6900"
help help
@ -243,6 +249,26 @@ config RTC_DRV_MAX8998
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called rtc-max8998. will be called rtc-max8998.
config RTC_DRV_MAX8997
tristate "Maxim MAX8997"
depends on MFD_MAX8997
help
If you say yes here you will get support for the
RTC of Maxim MAX8997 PMIC.
This driver can also be built as a module. If so, the module
will be called rtc-max8997.
config RTC_DRV_MAX77686
tristate "Maxim MAX77686"
depends on MFD_MAX77686
help
If you say yes here you will get support for the
RTC of Maxim MAX77686 PMIC.
This driver can also be built as a module. If so, the module
will be called rtc-max77686.
config RTC_DRV_RS5C372 config RTC_DRV_RS5C372
tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A" tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
help help
@ -380,6 +406,14 @@ config RTC_DRV_TPS65910
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called rtc-tps65910. will be called rtc-tps65910.
config RTC_DRV_TPS80031
tristate "TI TPS80031/TPS80032 RTC driver"
depends on MFD_TPS80031
help
TI Power Managment IC TPS80031 supports RTC functionality
along with alarm. This driver supports the RTC driver for
the TPS80031 RTC module.
config RTC_DRV_RC5T583 config RTC_DRV_RC5T583
tristate "RICOH 5T583 RTC driver" tristate "RICOH 5T583 RTC driver"
depends on MFD_RC5T583 depends on MFD_RC5T583
@ -537,6 +571,14 @@ config RTC_DRV_PCF2123
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called rtc-pcf2123. will be called rtc-pcf2123.
config RTC_DRV_RX4581
tristate "Epson RX-4581"
help
If you say yes here you will get support for the Epson RX-4581.
This driver can also be built as a module. If so the module
will be called rtc-rx4581.
endif # SPI_MASTER endif # SPI_MASTER
comment "Platform RTC drivers" comment "Platform RTC drivers"

View file

@ -58,6 +58,7 @@ obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o
obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o
obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
@ -71,7 +72,9 @@ obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
@ -97,6 +100,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
@ -115,6 +119,7 @@ obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o
obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
obj-$(CONFIG_RTC_DRV_TPS80031) += rtc-tps80031.o
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o

View file

@ -11,6 +11,8 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
@ -261,7 +263,7 @@ static int __init rtc_init(void)
{ {
rtc_class = class_create(THIS_MODULE, "rtc"); rtc_class = class_create(THIS_MODULE, "rtc");
if (IS_ERR(rtc_class)) { if (IS_ERR(rtc_class)) {
printk(KERN_ERR "%s: couldn't create class\n", __FILE__); pr_err("couldn't create class\n");
return PTR_ERR(rtc_class); return PTR_ERR(rtc_class);
} }
rtc_class->suspend = rtc_suspend; rtc_class->suspend = rtc_suspend;

View file

@ -86,7 +86,7 @@ static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year); tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
tm->tm_year = tm->tm_year - 1900; tm->tm_year = tm->tm_year - 1900;
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_hour, tm->tm_min, tm->tm_sec);
@ -100,7 +100,7 @@ static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
{ {
unsigned long cr; unsigned long cr;
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_hour, tm->tm_min, tm->tm_sec);
@ -145,7 +145,7 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->enabled = (at91_rtc_read(AT91_RTC_IMR) & AT91_RTC_ALARM) alrm->enabled = (at91_rtc_read(AT91_RTC_IMR) & AT91_RTC_ALARM)
? 1 : 0; ? 1 : 0;
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_hour, tm->tm_min, tm->tm_sec);
@ -183,7 +183,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM); at91_rtc_write(AT91_RTC_IER, AT91_RTC_ALARM);
} }
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__, dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec); tm.tm_min, tm.tm_sec);
@ -192,7 +192,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{ {
pr_debug("%s(): cmd=%08x\n", __func__, enabled); dev_dbg(dev, "%s(): cmd=%08x\n", __func__, enabled);
if (enabled) { if (enabled) {
at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
@ -240,7 +240,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
rtc_update_irq(rtc, 1, events); rtc_update_irq(rtc, 1, events);
pr_debug("%s(): num=%ld, events=0x%02lx\n", __func__, dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n", __func__,
events >> 8, events & 0x000000FF); events >> 8, events & 0x000000FF);
return IRQ_HANDLED; return IRQ_HANDLED;
@ -296,8 +296,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
IRQF_SHARED, IRQF_SHARED,
"at91_rtc", pdev); "at91_rtc", pdev);
if (ret) { if (ret) {
printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", dev_err(&pdev->dev, "IRQ %d already in use.\n", irq);
irq);
return ret; return ret;
} }
@ -315,7 +314,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
} }
platform_set_drvdata(pdev, rtc); platform_set_drvdata(pdev, rtc);
printk(KERN_INFO "AT91 Real Time Clock driver.\n"); dev_info(&pdev->dev, "AT91 Real Time Clock driver.\n");
return 0; return 0;
} }

View file

@ -706,7 +706,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
rtc_cmos_int_handler = hpet_rtc_interrupt; rtc_cmos_int_handler = hpet_rtc_interrupt;
err = hpet_register_irq_handler(cmos_interrupt); err = hpet_register_irq_handler(cmos_interrupt);
if (err != 0) { if (err != 0) {
printk(KERN_WARNING "hpet_register_irq_handler " dev_warn(dev, "hpet_register_irq_handler "
" failed in rtc_init()."); " failed in rtc_init().");
goto cleanup1; goto cleanup1;
} }
@ -731,8 +731,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
goto cleanup2; goto cleanup2;
} }
pr_info("%s: %s%s, %zd bytes nvram%s\n", dev_info(dev, "%s%s, %zd bytes nvram%s\n",
dev_name(&cmos_rtc.rtc->dev),
!is_valid_irq(rtc_irq) ? "no alarms" : !is_valid_irq(rtc_irq) ? "no alarms" :
cmos_rtc.mon_alrm ? "alarms up to one year" : cmos_rtc.mon_alrm ? "alarms up to one year" :
cmos_rtc.day_alrm ? "alarms up to one month" : cmos_rtc.day_alrm ? "alarms up to one month" :
@ -820,8 +819,7 @@ static int cmos_suspend(struct device *dev)
enable_irq_wake(cmos->irq); enable_irq_wake(cmos->irq);
} }
pr_debug("%s: suspend%s, ctrl %02x\n", dev_dbg(dev, "suspend%s, ctrl %02x\n",
dev_name(&cmos_rtc.rtc->dev),
(tmp & RTC_AIE) ? ", alarm may wake" : "", (tmp & RTC_AIE) ? ", alarm may wake" : "",
tmp); tmp);
@ -876,9 +874,7 @@ static int cmos_resume(struct device *dev)
spin_unlock_irq(&rtc_lock); spin_unlock_irq(&rtc_lock);
} }
pr_debug("%s: resume, ctrl %02x\n", dev_dbg(dev, "resume, ctrl %02x\n", tmp);
dev_name(&cmos_rtc.rtc->dev),
tmp);
return 0; return 0;
} }
@ -1098,7 +1094,6 @@ static __init void cmos_of_init(struct platform_device *pdev)
} }
#else #else
static inline void cmos_of_init(struct platform_device *pdev) {} static inline void cmos_of_init(struct platform_device *pdev) {}
#define of_cmos_match NULL
#endif #endif
/*----------------------------------------------------------------*/ /*----------------------------------------------------------------*/
@ -1140,7 +1135,7 @@ static struct platform_driver cmos_platform_driver = {
#ifdef CONFIG_PM #ifdef CONFIG_PM
.pm = &cmos_pm_ops, .pm = &cmos_pm_ops,
#endif #endif
.of_match_table = of_cmos_match, .of_match_table = of_match_ptr(of_cmos_match),
} }
}; };

View file

@ -157,7 +157,6 @@ static int __exit coh901331_remove(struct platform_device *pdev)
if (rtap) { if (rtap) {
rtc_device_unregister(rtap->rtc); rtc_device_unregister(rtap->rtc);
clk_unprepare(rtap->clk); clk_unprepare(rtap->clk);
clk_put(rtap->clk);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
} }
@ -196,7 +195,7 @@ static int __init coh901331_probe(struct platform_device *pdev)
"RTC COH 901 331 Alarm", rtap)) "RTC COH 901 331 Alarm", rtap))
return -EIO; return -EIO;
rtap->clk = clk_get(&pdev->dev, NULL); rtap->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(rtap->clk)) { if (IS_ERR(rtap->clk)) {
ret = PTR_ERR(rtap->clk); ret = PTR_ERR(rtap->clk);
dev_err(&pdev->dev, "could not get clock\n"); dev_err(&pdev->dev, "could not get clock\n");
@ -207,7 +206,7 @@ static int __init coh901331_probe(struct platform_device *pdev)
ret = clk_prepare_enable(rtap->clk); ret = clk_prepare_enable(rtap->clk);
if (ret) { if (ret) {
dev_err(&pdev->dev, "could not enable clock\n"); dev_err(&pdev->dev, "could not enable clock\n");
goto out_no_clk_prepenable; return ret;
} }
clk_disable(rtap->clk); clk_disable(rtap->clk);
@ -224,8 +223,6 @@ static int __init coh901331_probe(struct platform_device *pdev)
out_no_rtc: out_no_rtc:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
clk_unprepare(rtap->clk); clk_unprepare(rtap->clk);
out_no_clk_prepenable:
clk_put(rtap->clk);
return ret; return ret;
} }

View file

@ -240,9 +240,10 @@ static int da9052_rtc_probe(struct platform_device *pdev)
rtc->da9052 = dev_get_drvdata(pdev->dev.parent); rtc->da9052 = dev_get_drvdata(pdev->dev.parent);
platform_set_drvdata(pdev, rtc); platform_set_drvdata(pdev, rtc);
rtc->irq = platform_get_irq_byname(pdev, "ALM"); rtc->irq = platform_get_irq_byname(pdev, "ALM");
ret = request_threaded_irq(rtc->irq, NULL, da9052_rtc_irq, ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
IRQF_TRIGGER_LOW | IRQF_ONESHOT, da9052_rtc_irq,
"ALM", rtc); IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"ALM", rtc);
if (ret != 0) { if (ret != 0) {
rtc_err(rtc->da9052, "irq registration failed: %d\n", ret); rtc_err(rtc->da9052, "irq registration failed: %d\n", ret);
return ret; return ret;
@ -250,16 +251,10 @@ static int da9052_rtc_probe(struct platform_device *pdev)
rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&da9052_rtc_ops, THIS_MODULE); &da9052_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rtc)) { if (IS_ERR(rtc->rtc))
ret = PTR_ERR(rtc->rtc); return PTR_ERR(rtc->rtc);
goto err_free_irq;
}
return 0; return 0;
err_free_irq:
free_irq(rtc->irq, rtc);
return ret;
} }
static int da9052_rtc_remove(struct platform_device *pdev) static int da9052_rtc_remove(struct platform_device *pdev)
@ -267,7 +262,6 @@ static int da9052_rtc_remove(struct platform_device *pdev)
struct da9052_rtc *rtc = pdev->dev.platform_data; struct da9052_rtc *rtc = pdev->dev.platform_data;
rtc_device_unregister(rtc->rtc); rtc_device_unregister(rtc->rtc);
free_irq(rtc->irq, rtc);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
return 0; return 0;

View file

@ -506,19 +506,19 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
davinci_rtc->pbase = res->start; davinci_rtc->pbase = res->start;
davinci_rtc->base_size = resource_size(res); davinci_rtc->base_size = resource_size(res);
mem = request_mem_region(davinci_rtc->pbase, davinci_rtc->base_size, mem = devm_request_mem_region(dev, davinci_rtc->pbase,
pdev->name); davinci_rtc->base_size, pdev->name);
if (!mem) { if (!mem) {
dev_err(dev, "RTC registers at %08x are not free\n", dev_err(dev, "RTC registers at %08x are not free\n",
davinci_rtc->pbase); davinci_rtc->pbase);
return -EBUSY; return -EBUSY;
} }
davinci_rtc->base = ioremap(davinci_rtc->pbase, davinci_rtc->base_size); davinci_rtc->base = devm_ioremap(dev, davinci_rtc->pbase,
davinci_rtc->base_size);
if (!davinci_rtc->base) { if (!davinci_rtc->base) {
dev_err(dev, "unable to ioremap MEM resource\n"); dev_err(dev, "unable to ioremap MEM resource\n");
ret = -ENOMEM; return -ENOMEM;
goto fail2;
} }
platform_set_drvdata(pdev, davinci_rtc); platform_set_drvdata(pdev, davinci_rtc);
@ -529,7 +529,7 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
ret = PTR_ERR(davinci_rtc->rtc); ret = PTR_ERR(davinci_rtc->rtc);
dev_err(dev, "unable to register RTC device, err %d\n", dev_err(dev, "unable to register RTC device, err %d\n",
ret); ret);
goto fail3; goto fail1;
} }
rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, PRTCIF_INTFLG); rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, PRTCIF_INTFLG);
@ -539,11 +539,11 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CTRL); rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CTRL);
rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL); rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL);
ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt, ret = devm_request_irq(dev, davinci_rtc->irq, davinci_rtc_interrupt,
0, "davinci_rtc", davinci_rtc); 0, "davinci_rtc", davinci_rtc);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "unable to register davinci RTC interrupt\n"); dev_err(dev, "unable to register davinci RTC interrupt\n");
goto fail4; goto fail2;
} }
/* Enable interrupts */ /* Enable interrupts */
@ -557,13 +557,10 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
return 0; return 0;
fail4:
rtc_device_unregister(davinci_rtc->rtc);
fail3:
platform_set_drvdata(pdev, NULL);
iounmap(davinci_rtc->base);
fail2: fail2:
release_mem_region(davinci_rtc->pbase, davinci_rtc->base_size); rtc_device_unregister(davinci_rtc->rtc);
fail1:
platform_set_drvdata(pdev, NULL);
return ret; return ret;
} }
@ -575,13 +572,8 @@ static int davinci_rtc_remove(struct platform_device *pdev)
rtcif_write(davinci_rtc, 0, PRTCIF_INTEN); rtcif_write(davinci_rtc, 0, PRTCIF_INTEN);
free_irq(davinci_rtc->irq, davinci_rtc);
rtc_device_unregister(davinci_rtc->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); platform_set_drvdata(pdev, NULL);
return 0; return 0;

View file

@ -11,6 +11,8 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/sched.h> #include <linux/sched.h>
@ -462,7 +464,7 @@ void rtc_dev_prepare(struct rtc_device *rtc)
return; return;
if (rtc->id >= RTC_DEV_MAX) { if (rtc->id >= RTC_DEV_MAX) {
pr_debug("%s: too many RTC devices\n", rtc->name); dev_dbg(&rtc->dev, "%s: too many RTC devices\n", rtc->name);
return; return;
} }
@ -480,10 +482,10 @@ void rtc_dev_prepare(struct rtc_device *rtc)
void rtc_dev_add_device(struct rtc_device *rtc) void rtc_dev_add_device(struct rtc_device *rtc)
{ {
if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1)) if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
printk(KERN_WARNING "%s: failed to add char device %d:%d\n", dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
rtc->name, MAJOR(rtc_devt), rtc->id); rtc->name, MAJOR(rtc_devt), rtc->id);
else else
pr_debug("%s: dev (%d:%d)\n", rtc->name, dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,
MAJOR(rtc_devt), rtc->id); MAJOR(rtc_devt), rtc->id);
} }
@ -499,8 +501,7 @@ void __init rtc_dev_init(void)
err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc"); err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
if (err < 0) if (err < 0)
printk(KERN_ERR "%s: failed to allocate char dev region\n", pr_err("failed to allocate char dev region\n");
__FILE__);
} }
void __exit rtc_dev_exit(void) void __exit rtc_dev_exit(void)

View file

@ -635,9 +635,7 @@ static int ds1305_probe(struct spi_device *spi)
goto fail0; goto fail0;
} }
dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "read", ds1305->ctrl);
"read", ds1305->ctrl[0],
ds1305->ctrl[1], ds1305->ctrl[2]);
/* Sanity check register values ... partially compensating for the /* Sanity check register values ... partially compensating for the
* fact that SPI has no device handshake. A pullup on MISO would * fact that SPI has no device handshake. A pullup on MISO would
@ -723,9 +721,7 @@ static int ds1305_probe(struct spi_device *spi)
goto fail0; goto fail0;
} }
dev_dbg(&spi->dev, "ctrl %s: %02x %02x %02x\n", dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "write", ds1305->ctrl);
"write", ds1305->ctrl[0],
ds1305->ctrl[1], ds1305->ctrl[2]);
} }
/* see if non-Linux software set up AM/PM mode */ /* see if non-Linux software set up AM/PM mode */

View file

@ -322,12 +322,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
return -EIO; return -EIO;
} }
dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs);
"read",
ds1307->regs[0], ds1307->regs[1],
ds1307->regs[2], ds1307->regs[3],
ds1307->regs[4], ds1307->regs[5],
ds1307->regs[6]);
t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f); t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f);
t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f); t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f);
@ -398,9 +393,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
break; break;
} }
dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n", dev_dbg(dev, "%s: %7ph\n", "write", buf);
"write", buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6]);
result = ds1307->write_block_data(ds1307->client, result = ds1307->write_block_data(ds1307->client,
ds1307->offset, 7, buf); ds1307->offset, 7, buf);

View file

@ -70,7 +70,7 @@ static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev,
for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) { for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) {
err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name); err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name);
if (err) { if (err) {
printk(KERN_ERR "error mapping gpio %s: %d\n", dev_err(&pdev->dev, "error mapping gpio %s: %d\n",
ds2404_gpio[i].name, err); ds2404_gpio[i].name, err);
goto err_request; goto err_request;
} }
@ -177,7 +177,7 @@ static void ds2404_write_memory(struct device *dev, u16 offset,
for (i = 0; i < length; i++) { for (i = 0; i < length; i++) {
if (out[i] != ds2404_read_byte(dev)) { if (out[i] != ds2404_read_byte(dev)) {
printk(KERN_ERR "read invalid data\n"); dev_err(dev, "read invalid data\n");
return; return;
} }
} }
@ -283,19 +283,7 @@ static struct platform_driver rtc_device_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
}; };
module_platform_driver(rtc_device_driver);
static __init int ds2404_init(void)
{
return platform_driver_register(&rtc_device_driver);
}
static __exit void ds2404_exit(void)
{
platform_driver_unregister(&rtc_device_driver);
}
module_init(ds2404_init);
module_exit(ds2404_exit);
MODULE_DESCRIPTION("DS2404 RTC"); MODULE_DESCRIPTION("DS2404 RTC");
MODULE_AUTHOR("Sven Schnelle"); MODULE_AUTHOR("Sven Schnelle");

View file

@ -13,6 +13,8 @@
* *
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/time.h> #include <linux/time.h>
@ -47,7 +49,7 @@ compute_wday(efi_time_t *eft)
int ndays = 0; int ndays = 0;
if (eft->year < 1998) { if (eft->year < 1998) {
printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n"); pr_err("EFI year < 1998, invalid date\n");
return -1; return -1;
} }
@ -70,7 +72,7 @@ convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
eft->day = wtime->tm_mday; eft->day = wtime->tm_mday;
eft->hour = wtime->tm_hour; eft->hour = wtime->tm_hour;
eft->minute = wtime->tm_min; eft->minute = wtime->tm_min;
eft->second = wtime->tm_sec; eft->second = wtime->tm_sec;
eft->nanosecond = 0; eft->nanosecond = 0;
eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0; eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
eft->timezone = EFI_UNSPECIFIED_TIMEZONE; eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
@ -142,7 +144,7 @@ static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
*/ */
status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft); status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
printk(KERN_WARNING "write status is %d\n", (int)status); dev_warn(dev, "write status is %d\n", (int)status);
return status == EFI_SUCCESS ? 0 : -EINVAL; return status == EFI_SUCCESS ? 0 : -EINVAL;
} }
@ -157,7 +159,7 @@ static int efi_read_time(struct device *dev, struct rtc_time *tm)
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
/* should never happen */ /* should never happen */
printk(KERN_ERR "efitime: can't read time\n"); dev_err(dev, "can't read time\n");
return -EINVAL; return -EINVAL;
} }

View file

@ -116,17 +116,7 @@ static int fm3130_get_time(struct device *dev, struct rtc_time *t)
fm3130_rtc_mode(dev, FM3130_MODE_NORMAL); fm3130_rtc_mode(dev, FM3130_MODE_NORMAL);
dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x %02x" dev_dbg(dev, "%s: %15ph\n", "read", fm3130->regs);
"%02x %02x %02x %02x %02x %02x %02x\n",
"read",
fm3130->regs[0], fm3130->regs[1],
fm3130->regs[2], fm3130->regs[3],
fm3130->regs[4], fm3130->regs[5],
fm3130->regs[6], fm3130->regs[7],
fm3130->regs[8], fm3130->regs[9],
fm3130->regs[0xa], fm3130->regs[0xb],
fm3130->regs[0xc], fm3130->regs[0xd],
fm3130->regs[0xe]);
t->tm_sec = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); t->tm_sec = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
t->tm_min = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); t->tm_min = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
@ -175,12 +165,7 @@ static int fm3130_set_time(struct device *dev, struct rtc_time *t)
tmp = t->tm_year - 100; tmp = t->tm_year - 100;
buf[FM3130_RTC_YEARS] = bin2bcd(tmp); buf[FM3130_RTC_YEARS] = bin2bcd(tmp);
dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x" dev_dbg(dev, "%s: %15ph\n", "write", buf);
"%02x %02x %02x %02x %02x %02x %02x %02x\n",
"write", buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7],
buf[8], buf[9], buf[0xa], buf[0xb],
buf[0xc], buf[0xd], buf[0xe]);
fm3130_rtc_mode(dev, FM3130_MODE_WRITE); fm3130_rtc_mode(dev, FM3130_MODE_WRITE);
@ -517,18 +502,8 @@ bad_alarm:
bad_clock: bad_clock:
if (!fm3130->data_valid || !fm3130->alarm_valid) if (!fm3130->data_valid || !fm3130->alarm_valid)
dev_dbg(&client->dev, dev_dbg(&client->dev, "%s: %15ph\n", "bogus registers",
"%s: %02x %02x %02x %02x %02x %02x %02x %02x" fm3130->regs);
"%02x %02x %02x %02x %02x %02x %02x\n",
"bogus registers",
fm3130->regs[0], fm3130->regs[1],
fm3130->regs[2], fm3130->regs[3],
fm3130->regs[4], fm3130->regs[5],
fm3130->regs[6], fm3130->regs[7],
fm3130->regs[8], fm3130->regs[9],
fm3130->regs[0xa], fm3130->regs[0xb],
fm3130->regs[0xc], fm3130->regs[0xd],
fm3130->regs[0xe]);
/* We won't bail out here because we just got invalid data. /* We won't bail out here because we just got invalid data.
Time setting from u-boot doesn't work anyway */ Time setting from u-boot doesn't work anyway */

View file

@ -406,7 +406,7 @@ static int dryice_rtc_probe(struct platform_device *pdev)
mutex_init(&imxdi->write_mutex); mutex_init(&imxdi->write_mutex);
imxdi->clk = clk_get(&pdev->dev, NULL); imxdi->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(imxdi->clk)) if (IS_ERR(imxdi->clk))
return PTR_ERR(imxdi->clk); return PTR_ERR(imxdi->clk);
clk_prepare_enable(imxdi->clk); clk_prepare_enable(imxdi->clk);
@ -475,7 +475,6 @@ static int dryice_rtc_probe(struct platform_device *pdev)
err: err:
clk_disable_unprepare(imxdi->clk); clk_disable_unprepare(imxdi->clk);
clk_put(imxdi->clk);
return rc; return rc;
} }
@ -492,7 +491,6 @@ static int dryice_rtc_remove(struct platform_device *pdev)
rtc_device_unregister(imxdi->rtc); rtc_device_unregister(imxdi->rtc);
clk_disable_unprepare(imxdi->clk); clk_disable_unprepare(imxdi->clk);
clk_put(imxdi->clk);
return 0; return 0;
} }

View file

@ -227,7 +227,7 @@ static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
buf[ISL12022_REG_SC + i]); buf[ISL12022_REG_SC + i]);
if (ret) if (ret)
return -EIO; return -EIO;
}; }
return 0; return 0;
} }

338
drivers/rtc/rtc-lp8788.c Normal file
View file

@ -0,0 +1,338 @@
/*
* TI LP8788 MFD - rtc driver
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/err.h>
#include <linux/irqdomain.h>
#include <linux/mfd/lp8788.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/slab.h>
/* register address */
#define LP8788_INTEN_3 0x05
#define LP8788_RTC_UNLOCK 0x64
#define LP8788_RTC_SEC 0x70
#define LP8788_ALM1_SEC 0x77
#define LP8788_ALM1_EN 0x7D
#define LP8788_ALM2_SEC 0x7E
#define LP8788_ALM2_EN 0x84
/* mask/shift bits */
#define LP8788_INT_RTC_ALM1_M BIT(1) /* Addr 05h */
#define LP8788_INT_RTC_ALM1_S 1
#define LP8788_INT_RTC_ALM2_M BIT(2) /* Addr 05h */
#define LP8788_INT_RTC_ALM2_S 2
#define LP8788_ALM_EN_M BIT(7) /* Addr 7Dh or 84h */
#define LP8788_ALM_EN_S 7
#define DEFAULT_ALARM_SEL LP8788_ALARM_1
#define LP8788_MONTH_OFFSET 1
#define LP8788_BASE_YEAR 2000
#define MAX_WDAY_BITS 7
#define LP8788_WDAY_SET 1
#define RTC_UNLOCK 0x1
#define RTC_LATCH 0x2
#define ALARM_IRQ_FLAG (RTC_IRQF | RTC_AF)
enum lp8788_time {
LPTIME_SEC,
LPTIME_MIN,
LPTIME_HOUR,
LPTIME_MDAY,
LPTIME_MON,
LPTIME_YEAR,
LPTIME_WDAY,
LPTIME_MAX,
};
struct lp8788_rtc {
struct lp8788 *lp;
struct rtc_device *rdev;
enum lp8788_alarm_sel alarm;
int irq;
};
static const u8 addr_alarm_sec[LP8788_ALARM_MAX] = {
LP8788_ALM1_SEC,
LP8788_ALM2_SEC,
};
static const u8 addr_alarm_en[LP8788_ALARM_MAX] = {
LP8788_ALM1_EN,
LP8788_ALM2_EN,
};
static const u8 mask_alarm_en[LP8788_ALARM_MAX] = {
LP8788_INT_RTC_ALM1_M,
LP8788_INT_RTC_ALM2_M,
};
static const u8 shift_alarm_en[LP8788_ALARM_MAX] = {
LP8788_INT_RTC_ALM1_S,
LP8788_INT_RTC_ALM2_S,
};
static int _to_tm_wday(u8 lp8788_wday)
{
int i;
if (lp8788_wday == 0)
return 0;
/* lookup defined weekday from read register value */
for (i = 0; i < MAX_WDAY_BITS; i++) {
if ((lp8788_wday >> i) == LP8788_WDAY_SET)
break;
}
return i + 1;
}
static inline int _to_lp8788_wday(int tm_wday)
{
return LP8788_WDAY_SET << (tm_wday - 1);
}
static void lp8788_rtc_unlock(struct lp8788 *lp)
{
lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_UNLOCK);
lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_LATCH);
}
static int lp8788_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct lp8788_rtc *rtc = dev_get_drvdata(dev);
struct lp8788 *lp = rtc->lp;
u8 data[LPTIME_MAX];
int ret;
lp8788_rtc_unlock(lp);
ret = lp8788_read_multi_bytes(lp, LP8788_RTC_SEC, data, LPTIME_MAX);
if (ret)
return ret;
tm->tm_sec = data[LPTIME_SEC];
tm->tm_min = data[LPTIME_MIN];
tm->tm_hour = data[LPTIME_HOUR];
tm->tm_mday = data[LPTIME_MDAY];
tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET;
tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900;
tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]);
return 0;
}
static int lp8788_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct lp8788_rtc *rtc = dev_get_drvdata(dev);
struct lp8788 *lp = rtc->lp;
u8 data[LPTIME_MAX - 1];
int ret, i, year;
year = tm->tm_year + 1900 - LP8788_BASE_YEAR;
if (year < 0) {
dev_err(lp->dev, "invalid year: %d\n", year);
return -EINVAL;
}
/* because rtc weekday is a readonly register, do not update */
data[LPTIME_SEC] = tm->tm_sec;
data[LPTIME_MIN] = tm->tm_min;
data[LPTIME_HOUR] = tm->tm_hour;
data[LPTIME_MDAY] = tm->tm_mday;
data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET;
data[LPTIME_YEAR] = year;
for (i = 0; i < ARRAY_SIZE(data); i++) {
ret = lp8788_write_byte(lp, LP8788_RTC_SEC + i, data[i]);
if (ret)
return ret;
}
return 0;
}
static int lp8788_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct lp8788_rtc *rtc = dev_get_drvdata(dev);
struct lp8788 *lp = rtc->lp;
struct rtc_time *tm = &alarm->time;
u8 addr, data[LPTIME_MAX];
int ret;
addr = addr_alarm_sec[rtc->alarm];
ret = lp8788_read_multi_bytes(lp, addr, data, LPTIME_MAX);
if (ret)
return ret;
tm->tm_sec = data[LPTIME_SEC];
tm->tm_min = data[LPTIME_MIN];
tm->tm_hour = data[LPTIME_HOUR];
tm->tm_mday = data[LPTIME_MDAY];
tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET;
tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900;
tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]);
alarm->enabled = data[LPTIME_WDAY] & LP8788_ALM_EN_M;
return 0;
}
static int lp8788_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct lp8788_rtc *rtc = dev_get_drvdata(dev);
struct lp8788 *lp = rtc->lp;
struct rtc_time *tm = &alarm->time;
u8 addr, data[LPTIME_MAX];
int ret, i, year;
year = tm->tm_year + 1900 - LP8788_BASE_YEAR;
if (year < 0) {
dev_err(lp->dev, "invalid year: %d\n", year);
return -EINVAL;
}
data[LPTIME_SEC] = tm->tm_sec;
data[LPTIME_MIN] = tm->tm_min;
data[LPTIME_HOUR] = tm->tm_hour;
data[LPTIME_MDAY] = tm->tm_mday;
data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET;
data[LPTIME_YEAR] = year;
data[LPTIME_WDAY] = _to_lp8788_wday(tm->tm_wday);
for (i = 0; i < ARRAY_SIZE(data); i++) {
addr = addr_alarm_sec[rtc->alarm] + i;
ret = lp8788_write_byte(lp, addr, data[i]);
if (ret)
return ret;
}
alarm->enabled = 1;
addr = addr_alarm_en[rtc->alarm];
return lp8788_update_bits(lp, addr, LP8788_ALM_EN_M,
alarm->enabled << LP8788_ALM_EN_S);
}
static int lp8788_alarm_irq_enable(struct device *dev, unsigned int enable)
{
struct lp8788_rtc *rtc = dev_get_drvdata(dev);
struct lp8788 *lp = rtc->lp;
u8 mask, shift;
if (!rtc->irq)
return -EIO;
mask = mask_alarm_en[rtc->alarm];
shift = shift_alarm_en[rtc->alarm];
return lp8788_update_bits(lp, LP8788_INTEN_3, mask, enable << shift);
}
static const struct rtc_class_ops lp8788_rtc_ops = {
.read_time = lp8788_rtc_read_time,
.set_time = lp8788_rtc_set_time,
.read_alarm = lp8788_read_alarm,
.set_alarm = lp8788_set_alarm,
.alarm_irq_enable = lp8788_alarm_irq_enable,
};
static irqreturn_t lp8788_alarm_irq_handler(int irq, void *ptr)
{
struct lp8788_rtc *rtc = ptr;
rtc_update_irq(rtc->rdev, 1, ALARM_IRQ_FLAG);
return IRQ_HANDLED;
}
static int lp8788_alarm_irq_register(struct platform_device *pdev,
struct lp8788_rtc *rtc)
{
struct resource *r;
struct lp8788 *lp = rtc->lp;
struct irq_domain *irqdm = lp->irqdm;
int irq;
rtc->irq = 0;
/* even the alarm IRQ number is not specified, rtc time should work */
r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, LP8788_ALM_IRQ);
if (!r)
return 0;
if (rtc->alarm == LP8788_ALARM_1)
irq = r->start;
else
irq = r->end;
rtc->irq = irq_create_mapping(irqdm, irq);
return devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
lp8788_alarm_irq_handler,
0, LP8788_ALM_IRQ, rtc);
}
static int lp8788_rtc_probe(struct platform_device *pdev)
{
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
struct lp8788_rtc *rtc;
struct device *dev = &pdev->dev;
rtc = devm_kzalloc(dev, sizeof(struct lp8788_rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
rtc->lp = lp;
rtc->alarm = lp->pdata ? lp->pdata->alarm_sel : DEFAULT_ALARM_SEL;
platform_set_drvdata(pdev, rtc);
device_init_wakeup(dev, 1);
rtc->rdev = rtc_device_register("lp8788_rtc", dev,
&lp8788_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rdev)) {
dev_err(dev, "can not register rtc device\n");
return PTR_ERR(rtc->rdev);
}
if (lp8788_alarm_irq_register(pdev, rtc))
dev_warn(lp->dev, "no rtc irq handler\n");
return 0;
}
static int lp8788_rtc_remove(struct platform_device *pdev)
{
struct lp8788_rtc *rtc = platform_get_drvdata(pdev);
rtc_device_unregister(rtc->rdev);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver lp8788_rtc_driver = {
.probe = lp8788_rtc_probe,
.remove = lp8788_rtc_remove,
.driver = {
.name = LP8788_DEV_RTC,
.owner = THIS_MODULE,
},
};
module_platform_driver(lp8788_rtc_driver);
MODULE_DESCRIPTION("Texas Instruments LP8788 RTC Driver");
MODULE_AUTHOR("Milo Kim");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:lp8788-rtc");

641
drivers/rtc/rtc-max77686.c Normal file
View file

@ -0,0 +1,641 @@
/*
* RTC driver for Maxim MAX77686
*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
*
* based on rtc-max8997.c
*
* 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.
*
*/
#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/max77686-private.h>
#include <linux/irqdomain.h>
#include <linux/regmap.h>
/* RTC Control Register */
#define BCD_EN_SHIFT 0
#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
#define MODEL24_SHIFT 1
#define MODEL24_MASK (1 << MODEL24_SHIFT)
/* RTC Update Register1 */
#define RTC_UDR_SHIFT 0
#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
#define RTC_RBUDR_SHIFT 4
#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
/* WTSR and SMPL Register */
#define WTSRT_SHIFT 0
#define SMPLT_SHIFT 2
#define WTSR_EN_SHIFT 6
#define SMPL_EN_SHIFT 7
#define WTSRT_MASK (3 << WTSRT_SHIFT)
#define SMPLT_MASK (3 << SMPLT_SHIFT)
#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT)
#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT)
/* RTC Hour register */
#define HOUR_PM_SHIFT 6
#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
/* RTC Alarm Enable */
#define ALARM_ENABLE_SHIFT 7
#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
#define MAX77686_RTC_UPDATE_DELAY 16
#undef MAX77686_RTC_WTSR_SMPL
enum {
RTC_SEC = 0,
RTC_MIN,
RTC_HOUR,
RTC_WEEKDAY,
RTC_MONTH,
RTC_YEAR,
RTC_DATE,
RTC_NR_TIME
};
struct max77686_rtc_info {
struct device *dev;
struct max77686_dev *max77686;
struct i2c_client *rtc;
struct rtc_device *rtc_dev;
struct mutex lock;
struct regmap *regmap;
int virq;
int rtc_24hr_mode;
};
enum MAX77686_RTC_OP {
MAX77686_RTC_WRITE,
MAX77686_RTC_READ,
};
static inline int max77686_rtc_calculate_wday(u8 shifted)
{
int counter = -1;
while (shifted) {
shifted >>= 1;
counter++;
}
return counter;
}
static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
int rtc_24hr_mode)
{
tm->tm_sec = data[RTC_SEC] & 0x7f;
tm->tm_min = data[RTC_MIN] & 0x7f;
if (rtc_24hr_mode)
tm->tm_hour = data[RTC_HOUR] & 0x1f;
else {
tm->tm_hour = data[RTC_HOUR] & 0x0f;
if (data[RTC_HOUR] & HOUR_PM_MASK)
tm->tm_hour += 12;
}
tm->tm_wday = max77686_rtc_calculate_wday(data[RTC_WEEKDAY] & 0x7f);
tm->tm_mday = data[RTC_DATE] & 0x1f;
tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
tm->tm_yday = 0;
tm->tm_isdst = 0;
}
static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
{
data[RTC_SEC] = tm->tm_sec;
data[RTC_MIN] = tm->tm_min;
data[RTC_HOUR] = tm->tm_hour;
data[RTC_WEEKDAY] = 1 << tm->tm_wday;
data[RTC_DATE] = tm->tm_mday;
data[RTC_MONTH] = tm->tm_mon + 1;
data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0 ;
if (tm->tm_year < 100) {
pr_warn("%s: MAX77686 RTC cannot handle the year %d."
"Assume it's 2000.\n", __func__, 1900 + tm->tm_year);
return -EINVAL;
}
return 0;
}
static int max77686_rtc_update(struct max77686_rtc_info *info,
enum MAX77686_RTC_OP op)
{
int ret;
unsigned int data;
if (op == MAX77686_RTC_WRITE)
data = 1 << RTC_UDR_SHIFT;
else
data = 1 << RTC_RBUDR_SHIFT;
ret = regmap_update_bits(info->max77686->rtc_regmap,
MAX77686_RTC_UPDATE0, data, data);
if (ret < 0)
dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
__func__, ret, data);
else {
/* Minimum 16ms delay required before RTC update. */
msleep(MAX77686_RTC_UPDATE_DELAY);
}
return ret;
}
static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct max77686_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
int ret;
mutex_lock(&info->lock);
ret = max77686_rtc_update(info, MAX77686_RTC_READ);
if (ret < 0)
goto out;
ret = regmap_bulk_read(info->max77686->rtc_regmap,
MAX77686_RTC_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, ret);
goto out;
}
max77686_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
ret = rtc_valid_tm(tm);
out:
mutex_unlock(&info->lock);
return ret;
}
static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct max77686_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
int ret;
ret = max77686_rtc_tm_to_data(tm, data);
if (ret < 0)
return ret;
mutex_lock(&info->lock);
ret = regmap_bulk_write(info->max77686->rtc_regmap,
MAX77686_RTC_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
ret);
goto out;
}
ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
out:
mutex_unlock(&info->lock);
return ret;
}
static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max77686_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
unsigned int val;
int i, ret;
mutex_lock(&info->lock);
ret = max77686_rtc_update(info, MAX77686_RTC_READ);
if (ret < 0)
goto out;
ret = regmap_bulk_read(info->max77686->rtc_regmap,
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
__func__, __LINE__, ret);
goto out;
}
max77686_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
alrm->enabled = 0;
for (i = 0; i < RTC_NR_TIME; i++) {
if (data[i] & ALARM_ENABLE_MASK) {
alrm->enabled = 1;
break;
}
}
alrm->pending = 0;
ret = regmap_read(info->max77686->regmap, MAX77686_REG_STATUS1, &val);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
__func__, __LINE__, ret);
goto out;
}
if (val & (1 << 4)) /* RTCA1 */
alrm->pending = 1;
out:
mutex_unlock(&info->lock);
return 0;
}
static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
{
u8 data[RTC_NR_TIME];
int ret, i;
struct rtc_time tm;
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
ret = max77686_rtc_update(info, MAX77686_RTC_READ);
if (ret < 0)
goto out;
ret = regmap_bulk_read(info->max77686->rtc_regmap,
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
goto out;
}
max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
for (i = 0; i < RTC_NR_TIME; i++)
data[i] &= ~ALARM_ENABLE_MASK;
ret = regmap_bulk_write(info->max77686->rtc_regmap,
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
goto out;
}
ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
out:
return ret;
}
static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
{
u8 data[RTC_NR_TIME];
int ret;
struct rtc_time tm;
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
ret = max77686_rtc_update(info, MAX77686_RTC_READ);
if (ret < 0)
goto out;
ret = regmap_bulk_read(info->max77686->rtc_regmap,
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
goto out;
}
max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
if (data[RTC_MONTH] & 0xf)
data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
if (data[RTC_YEAR] & 0x7f)
data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
if (data[RTC_DATE] & 0x1f)
data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
ret = regmap_bulk_write(info->max77686->rtc_regmap,
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
goto out;
}
ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
out:
return ret;
}
static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max77686_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
int ret;
ret = max77686_rtc_tm_to_data(&alrm->time, data);
if (ret < 0)
return ret;
mutex_lock(&info->lock);
ret = max77686_rtc_stop_alarm(info);
if (ret < 0)
goto out;
ret = regmap_bulk_write(info->max77686->rtc_regmap,
MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
goto out;
}
ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
if (ret < 0)
goto out;
if (alrm->enabled)
ret = max77686_rtc_start_alarm(info);
out:
mutex_unlock(&info->lock);
return ret;
}
static int max77686_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
struct max77686_rtc_info *info = dev_get_drvdata(dev);
int ret;
mutex_lock(&info->lock);
if (enabled)
ret = max77686_rtc_start_alarm(info);
else
ret = max77686_rtc_stop_alarm(info);
mutex_unlock(&info->lock);
return ret;
}
static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data)
{
struct max77686_rtc_info *info = data;
dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
return IRQ_HANDLED;
}
static const struct rtc_class_ops max77686_rtc_ops = {
.read_time = max77686_rtc_read_time,
.set_time = max77686_rtc_set_time,
.read_alarm = max77686_rtc_read_alarm,
.set_alarm = max77686_rtc_set_alarm,
.alarm_irq_enable = max77686_rtc_alarm_irq_enable,
};
#ifdef MAX77686_RTC_WTSR_SMPL
static void max77686_rtc_enable_wtsr(struct max77686_rtc_info *info, bool enable)
{
int ret;
unsigned int val, mask;
if (enable)
val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT);
else
val = 0;
mask = WTSR_EN_MASK | WTSRT_MASK;
dev_info(info->dev, "%s: %s WTSR\n", __func__,
enable ? "enable" : "disable");
ret = regmap_update_bits(info->max77686->rtc_regmap,
MAX77686_WTSR_SMPL_CNTL, mask, val);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
__func__, ret);
return;
}
max77686_rtc_update(info, MAX77686_RTC_WRITE);
}
static void max77686_rtc_enable_smpl(struct max77686_rtc_info *info, bool enable)
{
int ret;
unsigned int val, mask;
if (enable)
val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT);
else
val = 0;
mask = SMPL_EN_MASK | SMPLT_MASK;
dev_info(info->dev, "%s: %s SMPL\n", __func__,
enable ? "enable" : "disable");
ret = regmap_update_bits(info->max77686->rtc_regmap,
MAX77686_WTSR_SMPL_CNTL, mask, val);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
__func__, ret);
return;
}
max77686_rtc_update(info, MAX77686_RTC_WRITE);
val = 0;
regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val);
pr_info("%s: WTSR_SMPL(0x%02x)\n", __func__, val);
}
#endif /* MAX77686_RTC_WTSR_SMPL */
static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
{
u8 data[2];
int ret;
/* Set RTC control register : Binary mode, 24hour mdoe */
data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
info->rtc_24hr_mode = 1;
ret = regmap_bulk_write(info->max77686->rtc_regmap, MAX77686_RTC_CONTROLM, data, 2);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
__func__, ret);
return ret;
}
ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
return ret;
}
static struct regmap_config max77686_rtc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int max77686_rtc_probe(struct platform_device *pdev)
{
struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent);
struct max77686_rtc_info *info;
int ret, virq;
dev_info(&pdev->dev, "%s\n", __func__);
info = kzalloc(sizeof(struct max77686_rtc_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
mutex_init(&info->lock);
info->dev = &pdev->dev;
info->max77686 = max77686;
info->rtc = max77686->rtc;
info->max77686->rtc_regmap = regmap_init_i2c(info->max77686->rtc,
&max77686_rtc_regmap_config);
if (IS_ERR(info->max77686->rtc_regmap)) {
ret = PTR_ERR(info->max77686->rtc_regmap);
dev_err(info->max77686->dev, "Failed to allocate register map: %d\n",
ret);
kfree(info);
return ret;
}
platform_set_drvdata(pdev, info);
ret = max77686_rtc_init_reg(info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
goto err_rtc;
}
#ifdef MAX77686_RTC_WTSR_SMPL
max77686_rtc_enable_wtsr(info, true);
max77686_rtc_enable_smpl(info, true);
#endif
device_init_wakeup(&pdev->dev, 1);
info->rtc_dev = rtc_device_register("max77686-rtc", &pdev->dev,
&max77686_rtc_ops, THIS_MODULE);
if (IS_ERR(info->rtc_dev)) {
dev_info(&pdev->dev, "%s: fail\n", __func__);
ret = PTR_ERR(info->rtc_dev);
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
if (ret == 0)
ret = -EINVAL;
goto err_rtc;
}
virq = irq_create_mapping(max77686->irq_domain, MAX77686_RTCIRQ_RTCA1);
if (!virq)
goto err_rtc;
info->virq = virq;
ret = request_threaded_irq(virq, NULL, max77686_rtc_alarm_irq, 0,
"rtc-alarm0", info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
info->virq, ret);
goto err_rtc;
}
goto out;
err_rtc:
kfree(info);
return ret;
out:
return ret;
}
static int max77686_rtc_remove(struct platform_device *pdev)
{
struct max77686_rtc_info *info = platform_get_drvdata(pdev);
if (info) {
free_irq(info->virq, info);
rtc_device_unregister(info->rtc_dev);
kfree(info);
}
return 0;
}
static void max77686_rtc_shutdown(struct platform_device *pdev)
{
#ifdef MAX77686_RTC_WTSR_SMPL
struct max77686_rtc_info *info = platform_get_drvdata(pdev);
int i;
u8 val = 0;
for (i = 0; i < 3; i++) {
max77686_rtc_enable_wtsr(info, false);
regmap_read(info->max77686->rtc_regmap, MAX77686_WTSR_SMPL_CNTL, &val);
pr_info("%s: WTSR_SMPL reg(0x%02x)\n", __func__, val);
if (val & WTSR_EN_MASK)
pr_emerg("%s: fail to disable WTSR\n", __func__);
else {
pr_info("%s: success to disable WTSR\n", __func__);
break;
}
}
/* Disable SMPL when power off */
max77686_rtc_enable_smpl(info, false);
#endif /* MAX77686_RTC_WTSR_SMPL */
}
static const struct platform_device_id rtc_id[] = {
{ "max77686-rtc", 0 },
{},
};
static struct platform_driver max77686_rtc_driver = {
.driver = {
.name = "max77686-rtc",
.owner = THIS_MODULE,
},
.probe = max77686_rtc_probe,
.remove = max77686_rtc_remove,
.shutdown = max77686_rtc_shutdown,
.id_table = rtc_id,
};
static int __init max77686_rtc_init(void)
{
return platform_driver_register(&max77686_rtc_driver);
}
module_init(max77686_rtc_init);
static void __exit max77686_rtc_exit(void)
{
platform_driver_unregister(&max77686_rtc_driver);
}
module_exit(max77686_rtc_exit);
MODULE_DESCRIPTION("Maxim MAX77686 RTC driver");
MODULE_AUTHOR("<woong.byun@samsung.com>");
MODULE_LICENSE("GPL");

View file

@ -205,8 +205,9 @@ static int max8907_rtc_probe(struct platform_device *pdev)
goto err_unregister; goto err_unregister;
} }
ret = request_threaded_irq(rtc->irq, NULL, max8907_irq_handler, ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
IRQF_ONESHOT, "max8907-alarm0", rtc); max8907_irq_handler,
IRQF_ONESHOT, "max8907-alarm0", rtc);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "Failed to request IRQ%d: %d\n", dev_err(&pdev->dev, "Failed to request IRQ%d: %d\n",
rtc->irq, ret); rtc->irq, ret);
@ -224,7 +225,6 @@ static int max8907_rtc_remove(struct platform_device *pdev)
{ {
struct max8907_rtc *rtc = platform_get_drvdata(pdev); struct max8907_rtc *rtc = platform_get_drvdata(pdev);
free_irq(rtc->irq, rtc);
rtc_device_unregister(rtc->rtc_dev); rtc_device_unregister(rtc->rtc_dev);
return 0; return 0;

552
drivers/rtc/rtc-max8997.c Normal file
View file

@ -0,0 +1,552 @@
/*
* RTC driver for Maxim MAX8997
*
* Copyright (C) 2013 Samsung Electronics Co.Ltd
*
* based on rtc-max8998.c
*
* 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.
*
*/
#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mfd/max8997-private.h>
#include <linux/irqdomain.h>
/* Module parameter for WTSR function control */
static int wtsr_en = 1;
module_param(wtsr_en, int, 0444);
MODULE_PARM_DESC(wtsr_en, "Wachdog Timeout & Sofware Reset (default=on)");
/* Module parameter for SMPL function control */
static int smpl_en = 1;
module_param(smpl_en, int, 0444);
MODULE_PARM_DESC(smpl_en, "Sudden Momentary Power Loss (default=on)");
/* RTC Control Register */
#define BCD_EN_SHIFT 0
#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
#define MODEL24_SHIFT 1
#define MODEL24_MASK (1 << MODEL24_SHIFT)
/* RTC Update Register1 */
#define RTC_UDR_SHIFT 0
#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
/* WTSR and SMPL Register */
#define WTSRT_SHIFT 0
#define SMPLT_SHIFT 2
#define WTSR_EN_SHIFT 6
#define SMPL_EN_SHIFT 7
#define WTSRT_MASK (3 << WTSRT_SHIFT)
#define SMPLT_MASK (3 << SMPLT_SHIFT)
#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT)
#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT)
/* RTC Hour register */
#define HOUR_PM_SHIFT 6
#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
/* RTC Alarm Enable */
#define ALARM_ENABLE_SHIFT 7
#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
enum {
RTC_SEC = 0,
RTC_MIN,
RTC_HOUR,
RTC_WEEKDAY,
RTC_MONTH,
RTC_YEAR,
RTC_DATE,
RTC_NR_TIME
};
struct max8997_rtc_info {
struct device *dev;
struct max8997_dev *max8997;
struct i2c_client *rtc;
struct rtc_device *rtc_dev;
struct mutex lock;
int virq;
int rtc_24hr_mode;
};
static void max8997_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
int rtc_24hr_mode)
{
tm->tm_sec = data[RTC_SEC] & 0x7f;
tm->tm_min = data[RTC_MIN] & 0x7f;
if (rtc_24hr_mode)
tm->tm_hour = data[RTC_HOUR] & 0x1f;
else {
tm->tm_hour = data[RTC_HOUR] & 0x0f;
if (data[RTC_HOUR] & HOUR_PM_MASK)
tm->tm_hour += 12;
}
tm->tm_wday = fls(data[RTC_WEEKDAY] & 0x7f) - 1;
tm->tm_mday = data[RTC_DATE] & 0x1f;
tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
tm->tm_yday = 0;
tm->tm_isdst = 0;
}
static int max8997_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
{
data[RTC_SEC] = tm->tm_sec;
data[RTC_MIN] = tm->tm_min;
data[RTC_HOUR] = tm->tm_hour;
data[RTC_WEEKDAY] = 1 << tm->tm_wday;
data[RTC_DATE] = tm->tm_mday;
data[RTC_MONTH] = tm->tm_mon + 1;
data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0 ;
if (tm->tm_year < 100) {
pr_warn("%s: MAX8997 RTC cannot handle the year %d."
"Assume it's 2000.\n", __func__, 1900 + tm->tm_year);
return -EINVAL;
}
return 0;
}
static inline int max8997_rtc_set_update_reg(struct max8997_rtc_info *info)
{
int ret;
ret = max8997_write_reg(info->rtc, MAX8997_RTC_UPDATE1,
RTC_UDR_MASK);
if (ret < 0)
dev_err(info->dev, "%s: fail to write update reg(%d)\n",
__func__, ret);
else {
/* Minimum 16ms delay required before RTC update.
* Otherwise, we may read and update based on out-of-date
* value */
msleep(20);
}
return ret;
}
static int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct max8997_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
int ret;
mutex_lock(&info->lock);
ret = max8997_bulk_read(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
mutex_unlock(&info->lock);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,
ret);
return ret;
}
max8997_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
return rtc_valid_tm(tm);
}
static int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct max8997_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
int ret;
ret = max8997_rtc_tm_to_data(tm, data);
if (ret < 0)
return ret;
mutex_lock(&info->lock);
ret = max8997_bulk_write(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
ret);
goto out;
}
ret = max8997_rtc_set_update_reg(info);
out:
mutex_unlock(&info->lock);
return ret;
}
static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max8997_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
u8 val;
int i, ret;
mutex_lock(&info->lock);
ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
data);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
__func__, __LINE__, ret);
goto out;
}
max8997_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
alrm->enabled = 0;
for (i = 0; i < RTC_NR_TIME; i++) {
if (data[i] & ALARM_ENABLE_MASK) {
alrm->enabled = 1;
break;
}
}
alrm->pending = 0;
ret = max8997_read_reg(info->max8997->i2c, MAX8997_REG_STATUS1, &val);
if (ret < 0) {
dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
__func__, __LINE__, ret);
goto out;
}
if (val & (1 << 4)) /* RTCA1 */
alrm->pending = 1;
out:
mutex_unlock(&info->lock);
return 0;
}
static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
{
u8 data[RTC_NR_TIME];
int ret, i;
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
data);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
goto out;
}
for (i = 0; i < RTC_NR_TIME; i++)
data[i] &= ~ALARM_ENABLE_MASK;
ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
data);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
goto out;
}
ret = max8997_rtc_set_update_reg(info);
out:
return ret;
}
static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
{
u8 data[RTC_NR_TIME];
int ret;
if (!mutex_is_locked(&info->lock))
dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
data);
if (ret < 0) {
dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
goto out;
}
data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
if (data[RTC_MONTH] & 0xf)
data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
if (data[RTC_YEAR] & 0x7f)
data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
if (data[RTC_DATE] & 0x1f)
data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
data);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
goto out;
}
ret = max8997_rtc_set_update_reg(info);
out:
return ret;
}
static int max8997_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max8997_rtc_info *info = dev_get_drvdata(dev);
u8 data[RTC_NR_TIME];
int ret;
ret = max8997_rtc_tm_to_data(&alrm->time, data);
if (ret < 0)
return ret;
dev_info(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d\n", __func__,
data[RTC_YEAR] + 2000, data[RTC_MONTH], data[RTC_DATE],
data[RTC_HOUR], data[RTC_MIN], data[RTC_SEC]);
mutex_lock(&info->lock);
ret = max8997_rtc_stop_alarm(info);
if (ret < 0)
goto out;
ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
data);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
goto out;
}
ret = max8997_rtc_set_update_reg(info);
if (ret < 0)
goto out;
if (alrm->enabled)
ret = max8997_rtc_start_alarm(info);
out:
mutex_unlock(&info->lock);
return ret;
}
static int max8997_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
struct max8997_rtc_info *info = dev_get_drvdata(dev);
int ret;
mutex_lock(&info->lock);
if (enabled)
ret = max8997_rtc_start_alarm(info);
else
ret = max8997_rtc_stop_alarm(info);
mutex_unlock(&info->lock);
return ret;
}
static irqreturn_t max8997_rtc_alarm_irq(int irq, void *data)
{
struct max8997_rtc_info *info = data;
dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
return IRQ_HANDLED;
}
static const struct rtc_class_ops max8997_rtc_ops = {
.read_time = max8997_rtc_read_time,
.set_time = max8997_rtc_set_time,
.read_alarm = max8997_rtc_read_alarm,
.set_alarm = max8997_rtc_set_alarm,
.alarm_irq_enable = max8997_rtc_alarm_irq_enable,
};
static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable)
{
int ret;
u8 val, mask;
if (!wtsr_en)
return;
if (enable)
val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT);
else
val = 0;
mask = WTSR_EN_MASK | WTSRT_MASK;
dev_info(info->dev, "%s: %s WTSR\n", __func__,
enable ? "enable" : "disable");
ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
__func__, ret);
return;
}
max8997_rtc_set_update_reg(info);
}
static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
{
int ret;
u8 val, mask;
if (!smpl_en)
return;
if (enable)
val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT);
else
val = 0;
mask = SMPL_EN_MASK | SMPLT_MASK;
dev_info(info->dev, "%s: %s SMPL\n", __func__,
enable ? "enable" : "disable");
ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
if (ret < 0) {
dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
__func__, ret);
return;
}
max8997_rtc_set_update_reg(info);
val = 0;
max8997_read_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, &val);
pr_info("%s: WTSR_SMPL(0x%02x)\n", __func__, val);
}
static int max8997_rtc_init_reg(struct max8997_rtc_info *info)
{
u8 data[2];
int ret;
/* Set RTC control register : Binary mode, 24hour mdoe */
data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
info->rtc_24hr_mode = 1;
ret = max8997_bulk_write(info->rtc, MAX8997_RTC_CTRLMASK, 2, data);
if (ret < 0) {
dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
__func__, ret);
return ret;
}
ret = max8997_rtc_set_update_reg(info);
return ret;
}
static int max8997_rtc_probe(struct platform_device *pdev)
{
struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent);
struct max8997_rtc_info *info;
int ret, virq;
info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_rtc_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
mutex_init(&info->lock);
info->dev = &pdev->dev;
info->max8997 = max8997;
info->rtc = max8997->rtc;
platform_set_drvdata(pdev, info);
ret = max8997_rtc_init_reg(info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
return ret;
}
max8997_rtc_enable_wtsr(info, true);
max8997_rtc_enable_smpl(info, true);
device_init_wakeup(&pdev->dev, 1);
info->rtc_dev = rtc_device_register("max8997-rtc", &pdev->dev,
&max8997_rtc_ops, THIS_MODULE);
if (IS_ERR(info->rtc_dev)) {
ret = PTR_ERR(info->rtc_dev);
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
return ret;
}
virq = irq_create_mapping(max8997->irq_domain, MAX8997_PMICIRQ_RTCA1);
if (!virq) {
dev_err(&pdev->dev, "Failed to create mapping alarm IRQ\n");
goto err_out;
}
info->virq = virq;
ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
max8997_rtc_alarm_irq, 0,
"rtc-alarm0", info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
info->virq, ret);
goto err_out;
}
return ret;
err_out:
rtc_device_unregister(info->rtc_dev);
return ret;
}
static int max8997_rtc_remove(struct platform_device *pdev)
{
struct max8997_rtc_info *info = platform_get_drvdata(pdev);
if (info)
rtc_device_unregister(info->rtc_dev);
return 0;
}
static void max8997_rtc_shutdown(struct platform_device *pdev)
{
struct max8997_rtc_info *info = platform_get_drvdata(pdev);
max8997_rtc_enable_wtsr(info, false);
max8997_rtc_enable_smpl(info, false);
}
static const struct platform_device_id rtc_id[] = {
{ "max8997-rtc", 0 },
{},
};
static struct platform_driver max8997_rtc_driver = {
.driver = {
.name = "max8997-rtc",
.owner = THIS_MODULE,
},
.probe = max8997_rtc_probe,
.remove = max8997_rtc_remove,
.shutdown = max8997_rtc_shutdown,
.id_table = rtc_id,
};
module_platform_driver(max8997_rtc_driver);
MODULE_DESCRIPTION("Maxim MAX8997 RTC driver");
MODULE_AUTHOR("<ms925.kim@samsung.com>");
MODULE_LICENSE("GPL");

View file

@ -13,6 +13,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/io.h> #include <linux/io.h>
@ -403,17 +404,19 @@ static int mpc5121_rtc_remove(struct platform_device *op)
return 0; return 0;
} }
#ifdef CONFIG_OF
static struct of_device_id mpc5121_rtc_match[] = { static struct of_device_id mpc5121_rtc_match[] = {
{ .compatible = "fsl,mpc5121-rtc", }, { .compatible = "fsl,mpc5121-rtc", },
{ .compatible = "fsl,mpc5200-rtc", }, { .compatible = "fsl,mpc5200-rtc", },
{}, {},
}; };
#endif
static struct platform_driver mpc5121_rtc_driver = { static struct platform_driver mpc5121_rtc_driver = {
.driver = { .driver = {
.name = "mpc5121-rtc", .name = "mpc5121-rtc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = mpc5121_rtc_match, .of_match_table = of_match_ptr(mpc5121_rtc_match),
}, },
.probe = mpc5121_rtc_probe, .probe = mpc5121_rtc_probe,
.remove = mpc5121_rtc_remove, .remove = mpc5121_rtc_remove,

View file

@ -23,6 +23,7 @@
#define REG_CONTROL3_PM_VDD (1 << 6) /* switch-over disabled */ #define REG_CONTROL3_PM_VDD (1 << 6) /* switch-over disabled */
#define REG_CONTROL3_PM_DSM (1 << 5) /* direct switching mode */ #define REG_CONTROL3_PM_DSM (1 << 5) /* direct switching mode */
#define REG_CONTROL3_PM_MASK 0xe0 #define REG_CONTROL3_PM_MASK 0xe0
#define REG_CONTROL3_BLF (1 << 2) /* battery low bit, read-only */
#define REG_SECONDS 0x03 #define REG_SECONDS 0x03
#define REG_SECONDS_OS (1 << 7) #define REG_SECONDS_OS (1 << 7)
@ -250,9 +251,39 @@ static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm)
return pcf8523_start_rtc(client); return pcf8523_start_rtc(client);
} }
#ifdef CONFIG_RTC_INTF_DEV
static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
struct i2c_client *client = to_i2c_client(dev);
u8 value;
int ret = 0, err;
switch (cmd) {
case RTC_VL_READ:
err = pcf8523_read(client, REG_CONTROL3, &value);
if (err < 0)
return err;
if (value & REG_CONTROL3_BLF)
ret = 1;
if (copy_to_user((void __user *)arg, &ret, sizeof(int)))
return -EFAULT;
return 0;
default:
return -ENOIOCTLCMD;
}
}
#else
#define pcf8523_rtc_ioctl NULL
#endif
static const struct rtc_class_ops pcf8523_rtc_ops = { static const struct rtc_class_ops pcf8523_rtc_ops = {
.read_time = pcf8523_rtc_read_time, .read_time = pcf8523_rtc_read_time,
.set_time = pcf8523_rtc_set_time, .set_time = pcf8523_rtc_set_time,
.ioctl = pcf8523_rtc_ioctl,
}; };
static int pcf8523_probe(struct i2c_client *client, static int pcf8523_probe(struct i2c_client *client,

View file

@ -181,7 +181,7 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
__func__, err, data[0], data[1]); __func__, err, data[0], data[1]);
return -EIO; return -EIO;
} }
}; }
return 0; return 0;
} }

View file

@ -185,8 +185,8 @@ static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm)
if (ctrl & (CTRL_STOP | CTRL_HOLD)) { if (ctrl & (CTRL_STOP | CTRL_HOLD)) {
unsigned char new_ctrl = ctrl & ~(CTRL_STOP | CTRL_HOLD); unsigned char new_ctrl = ctrl & ~(CTRL_STOP | CTRL_HOLD);
printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n", dev_warn(dev, "resetting control %02x -> %02x\n",
ctrl, new_ctrl); ctrl, new_ctrl);
if ((err = pcf8583_set_ctrl(client, &new_ctrl)) < 0) if ((err = pcf8583_set_ctrl(client, &new_ctrl)) < 0)
return err; return err;

View file

@ -384,6 +384,8 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
goto out_no_irq; goto out_no_irq;
} }
device_init_wakeup(&adev->dev, 1);
return 0; return 0;
out_no_irq: out_no_irq:

View file

@ -62,6 +62,10 @@
#define RYxR_MONTH_S 5 #define RYxR_MONTH_S 5
#define RYxR_MONTH_MASK (0xf << RYxR_MONTH_S) #define RYxR_MONTH_MASK (0xf << RYxR_MONTH_S)
#define RYxR_DAY_MASK 0x1f #define RYxR_DAY_MASK 0x1f
#define RDxR_WOM_S 20
#define RDxR_WOM_MASK (0x7 << RDxR_WOM_S)
#define RDxR_DOW_S 17
#define RDxR_DOW_MASK (0x7 << RDxR_DOW_S)
#define RDxR_HOUR_S 12 #define RDxR_HOUR_S 12
#define RDxR_HOUR_MASK (0x1f << RDxR_HOUR_S) #define RDxR_HOUR_MASK (0x1f << RDxR_HOUR_S)
#define RDxR_MIN_S 6 #define RDxR_MIN_S 6
@ -91,6 +95,7 @@ struct pxa_rtc {
spinlock_t lock; /* Protects this structure */ spinlock_t lock; /* Protects this structure */
}; };
static u32 ryxr_calc(struct rtc_time *tm) static u32 ryxr_calc(struct rtc_time *tm)
{ {
return ((tm->tm_year + 1900) << RYxR_YEAR_S) return ((tm->tm_year + 1900) << RYxR_YEAR_S)
@ -100,7 +105,10 @@ static u32 ryxr_calc(struct rtc_time *tm)
static u32 rdxr_calc(struct rtc_time *tm) static u32 rdxr_calc(struct rtc_time *tm)
{ {
return (tm->tm_hour << RDxR_HOUR_S) | (tm->tm_min << RDxR_MIN_S) return ((((tm->tm_mday + 6) / 7) << RDxR_WOM_S) & RDxR_WOM_MASK)
| (((tm->tm_wday + 1) << RDxR_DOW_S) & RDxR_DOW_MASK)
| (tm->tm_hour << RDxR_HOUR_S)
| (tm->tm_min << RDxR_MIN_S)
| tm->tm_sec; | tm->tm_sec;
} }
@ -109,6 +117,7 @@ static void tm_calc(u32 rycr, u32 rdcr, struct rtc_time *tm)
tm->tm_year = ((rycr & RYxR_YEAR_MASK) >> RYxR_YEAR_S) - 1900; tm->tm_year = ((rycr & RYxR_YEAR_MASK) >> RYxR_YEAR_S) - 1900;
tm->tm_mon = (((rycr & RYxR_MONTH_MASK) >> RYxR_MONTH_S)) - 1; tm->tm_mon = (((rycr & RYxR_MONTH_MASK) >> RYxR_MONTH_S)) - 1;
tm->tm_mday = (rycr & RYxR_DAY_MASK); tm->tm_mday = (rycr & RYxR_DAY_MASK);
tm->tm_wday = ((rycr & RDxR_DOW_MASK) >> RDxR_DOW_S) - 1;
tm->tm_hour = (rdcr & RDxR_HOUR_MASK) >> RDxR_HOUR_S; tm->tm_hour = (rdcr & RDxR_HOUR_MASK) >> RDxR_HOUR_S;
tm->tm_min = (rdcr & RDxR_MIN_MASK) >> RDxR_MIN_S; tm->tm_min = (rdcr & RDxR_MIN_MASK) >> RDxR_MIN_S;
tm->tm_sec = rdcr & RDxR_SEC_MASK; tm->tm_sec = rdcr & RDxR_SEC_MASK;
@ -300,8 +309,6 @@ static int pxa_rtc_proc(struct device *dev, struct seq_file *seq)
} }
static const struct rtc_class_ops pxa_rtc_ops = { static const struct rtc_class_ops pxa_rtc_ops = {
.open = pxa_rtc_open,
.release = pxa_rtc_release,
.read_time = pxa_rtc_read_time, .read_time = pxa_rtc_read_time,
.set_time = pxa_rtc_set_time, .set_time = pxa_rtc_set_time,
.read_alarm = pxa_rtc_read_alarm, .read_alarm = pxa_rtc_read_alarm,
@ -341,7 +348,7 @@ static int __init pxa_rtc_probe(struct platform_device *pdev)
dev_err(dev, "No alarm IRQ resource defined\n"); dev_err(dev, "No alarm IRQ resource defined\n");
goto err_ress; goto err_ress;
} }
pxa_rtc_open(dev);
ret = -ENOMEM; ret = -ENOMEM;
pxa_rtc->base = ioremap(pxa_rtc->ress->start, pxa_rtc->base = ioremap(pxa_rtc->ress->start,
resource_size(pxa_rtc->ress)); resource_size(pxa_rtc->ress));
@ -387,6 +394,9 @@ static int __exit pxa_rtc_remove(struct platform_device *pdev)
{ {
struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev); struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
pxa_rtc_release(dev);
rtc_device_unregister(pxa_rtc->rtc); rtc_device_unregister(pxa_rtc->rtc);
spin_lock_irq(&pxa_rtc->lock); spin_lock_irq(&pxa_rtc->lock);
@ -444,10 +454,7 @@ static struct platform_driver pxa_rtc_driver = {
static int __init pxa_rtc_init(void) static int __init pxa_rtc_init(void)
{ {
if (cpu_is_pxa27x() || cpu_is_pxa3xx()) return platform_driver_probe(&pxa_rtc_driver, pxa_rtc_probe);
return platform_driver_probe(&pxa_rtc_driver, pxa_rtc_probe);
return -ENODEV;
} }
static void __exit pxa_rtc_exit(void) static void __exit pxa_rtc_exit(void)

View file

@ -39,6 +39,8 @@
* 1.13 Nobuhiro Iwamatsu: Updata driver. * 1.13 Nobuhiro Iwamatsu: Updata driver.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/rtc.h> #include <linux/rtc.h>
@ -352,8 +354,7 @@ static void rs5c313_check_xstp_bit(void)
tm.tm_year = 2000 - 1900; tm.tm_year = 2000 - 1900;
rs5c313_rtc_set_time(NULL, &tm); rs5c313_rtc_set_time(NULL, &tm);
printk(KERN_ERR "RICHO RS5C313: invalid value, resetting to " pr_err("invalid value, resetting to 1 Jan 2000\n");
"1 Jan 2000\n");
} }
RS5C313_CEDISABLE; RS5C313_CEDISABLE;
ndelay(700); /* CE:L */ ndelay(700); /* CE:L */

View file

@ -311,8 +311,7 @@ static int rs5c_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
buf &= ~RS5C_CTRL1_AALE; buf &= ~RS5C_CTRL1_AALE;
if (i2c_smbus_write_byte_data(client, addr, buf) < 0) { if (i2c_smbus_write_byte_data(client, addr, buf) < 0) {
printk(KERN_WARNING "%s: can't update alarm\n", dev_warn(dev, "can't update alarm\n");
rs5c->rtc->name);
status = -EIO; status = -EIO;
} else } else
rs5c->regs[RS5C_REG_CTRL1] = buf; rs5c->regs[RS5C_REG_CTRL1] = buf;
@ -381,7 +380,7 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
addr = RS5C_ADDR(RS5C_REG_CTRL1); addr = RS5C_ADDR(RS5C_REG_CTRL1);
buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE; buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE;
if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) { if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) {
pr_debug("%s: can't disable alarm\n", rs5c->rtc->name); dev_dbg(dev, "can't disable alarm\n");
return -EIO; return -EIO;
} }
rs5c->regs[RS5C_REG_CTRL1] = buf[0]; rs5c->regs[RS5C_REG_CTRL1] = buf[0];
@ -395,7 +394,7 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
for (i = 0; i < sizeof(buf); i++) { for (i = 0; i < sizeof(buf); i++) {
addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i); addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) { if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) {
pr_debug("%s: can't set alarm time\n", rs5c->rtc->name); dev_dbg(dev, "can't set alarm time\n");
return -EIO; return -EIO;
} }
} }
@ -405,8 +404,7 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
addr = RS5C_ADDR(RS5C_REG_CTRL1); addr = RS5C_ADDR(RS5C_REG_CTRL1);
buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE; buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE;
if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0)
printk(KERN_WARNING "%s: can't enable alarm\n", dev_warn(dev, "can't enable alarm\n");
rs5c->rtc->name);
rs5c->regs[RS5C_REG_CTRL1] = buf[0]; rs5c->regs[RS5C_REG_CTRL1] = buf[0];
} }

314
drivers/rtc/rtc-rx4581.c Normal file
View file

@ -0,0 +1,314 @@
/* drivers/rtc/rtc-rx4581.c
*
* written by Torben Hohn <torbenh@linutronix.de>
*
* Based on:
* drivers/rtc/rtc-max6902.c
*
* Copyright (C) 2006 8D Technologies inc.
* Copyright (C) 2004 Compulab Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Driver for MAX6902 spi RTC
*
* and based on:
* drivers/rtc/rtc-rx8581.c
*
* An I2C driver for the Epson RX8581 RTC
*
* Author: Martyn Welch <martyn.welch@ge.com>
* Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC)
* Copyright 2005-06 Tower Technologies
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/bcd.h>
#define RX4581_REG_SC 0x00 /* Second in BCD */
#define RX4581_REG_MN 0x01 /* Minute in BCD */
#define RX4581_REG_HR 0x02 /* Hour in BCD */
#define RX4581_REG_DW 0x03 /* Day of Week */
#define RX4581_REG_DM 0x04 /* Day of Month in BCD */
#define RX4581_REG_MO 0x05 /* Month in BCD */
#define RX4581_REG_YR 0x06 /* Year in BCD */
#define RX4581_REG_RAM 0x07 /* RAM */
#define RX4581_REG_AMN 0x08 /* Alarm Min in BCD*/
#define RX4581_REG_AHR 0x09 /* Alarm Hour in BCD */
#define RX4581_REG_ADM 0x0A
#define RX4581_REG_ADW 0x0A
#define RX4581_REG_TMR0 0x0B
#define RX4581_REG_TMR1 0x0C
#define RX4581_REG_EXT 0x0D /* Extension Register */
#define RX4581_REG_FLAG 0x0E /* Flag Register */
#define RX4581_REG_CTRL 0x0F /* Control Register */
/* Flag Register bit definitions */
#define RX4581_FLAG_UF 0x20 /* Update */
#define RX4581_FLAG_TF 0x10 /* Timer */
#define RX4581_FLAG_AF 0x08 /* Alarm */
#define RX4581_FLAG_VLF 0x02 /* Voltage Low */
/* Control Register bit definitions */
#define RX4581_CTRL_UIE 0x20 /* Update Interrupt Enable */
#define RX4581_CTRL_TIE 0x10 /* Timer Interrupt Enable */
#define RX4581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */
#define RX4581_CTRL_STOP 0x02 /* STOP bit */
#define RX4581_CTRL_RESET 0x01 /* RESET bit */
static int rx4581_set_reg(struct device *dev, unsigned char address,
unsigned char data)
{
struct spi_device *spi = to_spi_device(dev);
unsigned char buf[2];
/* high nibble must be '0' to write */
buf[0] = address & 0x0f;
buf[1] = data;
return spi_write_then_read(spi, buf, 2, NULL, 0);
}
static int rx4581_get_reg(struct device *dev, unsigned char address,
unsigned char *data)
{
struct spi_device *spi = to_spi_device(dev);
/* Set MSB to indicate read */
*data = address | 0x80;
return spi_write_then_read(spi, data, 1, data, 1);
}
/*
* In the routines that deal directly with the rx8581 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
*/
static int rx4581_get_datetime(struct device *dev, struct rtc_time *tm)
{
struct spi_device *spi = to_spi_device(dev);
unsigned char date[7];
unsigned char data;
int err;
/* First we ensure that the "update flag" is not set, we read the
* time and date then re-read the "update flag". If the update flag
* has been set, we know that the time has changed during the read so
* we repeat the whole process again.
*/
err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data);
if (err != 0) {
dev_err(dev, "Unable to read device flags\n");
return -EIO;
}
do {
/* If update flag set, clear it */
if (data & RX4581_FLAG_UF) {
err = rx4581_set_reg(dev,
RX4581_REG_FLAG, (data & ~RX4581_FLAG_UF));
if (err != 0) {
dev_err(dev, "Unable to write device "
"flags\n");
return -EIO;
}
}
/* Now read time and date */
date[0] = 0x80;
err = spi_write_then_read(spi, date, 1, date, 7);
if (err < 0) {
dev_err(dev, "Unable to read date\n");
return -EIO;
}
/* Check flag register */
err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data);
if (err != 0) {
dev_err(dev, "Unable to read device flags\n");
return -EIO;
}
} while (data & RX4581_FLAG_UF);
if (data & RX4581_FLAG_VLF)
dev_info(dev,
"low voltage detected, date/time is not reliable.\n");
dev_dbg(dev,
"%s: raw data is sec=%02x, min=%02x, hr=%02x, "
"wday=%02x, mday=%02x, mon=%02x, year=%02x\n",
__func__,
date[0], date[1], date[2], date[3], date[4], date[5], date[6]);
tm->tm_sec = bcd2bin(date[RX4581_REG_SC] & 0x7F);
tm->tm_min = bcd2bin(date[RX4581_REG_MN] & 0x7F);
tm->tm_hour = bcd2bin(date[RX4581_REG_HR] & 0x3F); /* rtc hr 0-23 */
tm->tm_wday = ilog2(date[RX4581_REG_DW] & 0x7F);
tm->tm_mday = bcd2bin(date[RX4581_REG_DM] & 0x3F);
tm->tm_mon = bcd2bin(date[RX4581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = bcd2bin(date[RX4581_REG_YR]);
if (tm->tm_year < 70)
tm->tm_year += 100; /* assume we are in 1970...2069 */
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
err = rtc_valid_tm(tm);
if (err < 0)
dev_err(dev, "retrieved date/time is not valid.\n");
return err;
}
static int rx4581_set_datetime(struct device *dev, struct rtc_time *tm)
{
struct spi_device *spi = to_spi_device(dev);
int err;
unsigned char buf[8], data;
dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
buf[0] = 0x00;
/* hours, minutes and seconds */
buf[RX4581_REG_SC+1] = bin2bcd(tm->tm_sec);
buf[RX4581_REG_MN+1] = bin2bcd(tm->tm_min);
buf[RX4581_REG_HR+1] = bin2bcd(tm->tm_hour);
buf[RX4581_REG_DM+1] = bin2bcd(tm->tm_mday);
/* month, 1 - 12 */
buf[RX4581_REG_MO+1] = bin2bcd(tm->tm_mon + 1);
/* year and century */
buf[RX4581_REG_YR+1] = bin2bcd(tm->tm_year % 100);
buf[RX4581_REG_DW+1] = (0x1 << tm->tm_wday);
/* Stop the clock */
err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data);
if (err != 0) {
dev_err(dev, "Unable to read control register\n");
return -EIO;
}
err = rx4581_set_reg(dev, RX4581_REG_CTRL,
(data | RX4581_CTRL_STOP));
if (err != 0) {
dev_err(dev, "Unable to write control register\n");
return -EIO;
}
/* write register's data */
err = spi_write_then_read(spi, buf, 8, NULL, 0);
if (err != 0) {
dev_err(dev, "Unable to write to date registers\n");
return -EIO;
}
/* get VLF and clear it */
err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data);
if (err != 0) {
dev_err(dev, "Unable to read flag register\n");
return -EIO;
}
err = rx4581_set_reg(dev, RX4581_REG_FLAG,
(data & ~(RX4581_FLAG_VLF)));
if (err != 0) {
dev_err(dev, "Unable to write flag register\n");
return -EIO;
}
/* Restart the clock */
err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data);
if (err != 0) {
dev_err(dev, "Unable to read control register\n");
return -EIO;
}
err = rx4581_set_reg(dev, RX4581_REG_CTRL,
(data & ~(RX4581_CTRL_STOP)));
if (err != 0) {
dev_err(dev, "Unable to write control register\n");
return -EIO;
}
return 0;
}
static const struct rtc_class_ops rx4581_rtc_ops = {
.read_time = rx4581_get_datetime,
.set_time = rx4581_set_datetime,
};
static int rx4581_probe(struct spi_device *spi)
{
struct rtc_device *rtc;
unsigned char tmp;
int res;
res = rx4581_get_reg(&spi->dev, RX4581_REG_SC, &tmp);
if (res != 0)
return res;
rtc = rtc_device_register("rx4581",
&spi->dev, &rx4581_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
dev_set_drvdata(&spi->dev, rtc);
return 0;
}
static int rx4581_remove(struct spi_device *spi)
{
struct rtc_device *rtc = dev_get_drvdata(&spi->dev);
rtc_device_unregister(rtc);
return 0;
}
static const struct spi_device_id rx4581_id[] = {
{ "rx4581", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, rx4581_id);
static struct spi_driver rx4581_driver = {
.driver = {
.name = "rtc-rx4581",
.owner = THIS_MODULE,
},
.probe = rx4581_probe,
.remove = rx4581_remove,
.id_table = rx4581_id,
};
module_spi_driver(rx4581_driver);
MODULE_DESCRIPTION("rx4581 spi RTC driver");
MODULE_AUTHOR("Torben Hohn");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:rtc-rx4581");

View file

@ -115,7 +115,7 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
{ {
unsigned int tmp; unsigned int tmp;
pr_debug("%s: aie=%d\n", __func__, enabled); dev_dbg(dev, "%s: aie=%d\n", __func__, enabled);
clk_enable(rtc_clk); clk_enable(rtc_clk);
tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
@ -203,7 +203,7 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
rtc_tm->tm_year += 100; rtc_tm->tm_year += 100;
pr_debug("read time %04d.%02d.%02d %02d:%02d:%02d\n", dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
@ -218,7 +218,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
void __iomem *base = s3c_rtc_base; void __iomem *base = s3c_rtc_base;
int year = tm->tm_year - 100; int year = tm->tm_year - 100;
pr_debug("set time %04d.%02d.%02d %02d:%02d:%02d\n", dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_hour, tm->tm_min, tm->tm_sec);
@ -259,7 +259,7 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
pr_debug("read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n", dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
alm_en, alm_en,
1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
@ -310,7 +310,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
unsigned int alrm_en; unsigned int alrm_en;
clk_enable(rtc_clk); clk_enable(rtc_clk);
pr_debug("s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
alrm->enabled, alrm->enabled,
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec); tm->tm_hour, tm->tm_min, tm->tm_sec);
@ -333,7 +333,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR);
} }
pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en); dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en);
writeb(alrm_en, base + S3C2410_RTCALM); writeb(alrm_en, base + S3C2410_RTCALM);
@ -459,7 +459,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
int ret; int ret;
int tmp; int tmp;
pr_debug("%s: probe=%p\n", __func__, pdev); dev_dbg(&pdev->dev, "%s: probe=%p\n", __func__, pdev);
/* find the IRQs */ /* find the IRQs */
@ -475,7 +475,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
return s3c_rtc_alarmno; return s3c_rtc_alarmno;
} }
pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n", dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
s3c_rtc_tickno, s3c_rtc_alarmno); s3c_rtc_tickno, s3c_rtc_alarmno);
/* get the memory region */ /* get the memory region */
@ -504,7 +504,7 @@ static int s3c_rtc_probe(struct platform_device *pdev)
s3c_rtc_enable(pdev, 1); s3c_rtc_enable(pdev, 1);
pr_debug("s3c2410_rtc: RTCCON=%02x\n", dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
readw(s3c_rtc_base + S3C2410_RTCCON)); readw(s3c_rtc_base + S3C2410_RTCCON));
device_init_wakeup(&pdev->dev, 1); device_init_wakeup(&pdev->dev, 1);

View file

@ -108,9 +108,6 @@ static int sa1100_rtc_open(struct device *dev)
struct rtc_device *rtc = info->rtc; struct rtc_device *rtc = info->rtc;
int ret; int ret;
ret = clk_prepare_enable(info->clk);
if (ret)
goto fail_clk;
ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev); ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev);
if (ret) { if (ret) {
dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz); dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz);
@ -130,7 +127,6 @@ static int sa1100_rtc_open(struct device *dev)
free_irq(info->irq_1hz, dev); free_irq(info->irq_1hz, dev);
fail_ui: fail_ui:
clk_disable_unprepare(info->clk); clk_disable_unprepare(info->clk);
fail_clk:
return ret; return ret;
} }
@ -144,7 +140,6 @@ static void sa1100_rtc_release(struct device *dev)
free_irq(info->irq_alarm, dev); free_irq(info->irq_alarm, dev);
free_irq(info->irq_1hz, dev); free_irq(info->irq_1hz, dev);
clk_disable_unprepare(info->clk);
} }
static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
@ -253,6 +248,9 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
spin_lock_init(&info->lock); spin_lock_init(&info->lock);
platform_set_drvdata(pdev, info); platform_set_drvdata(pdev, info);
ret = clk_prepare_enable(info->clk);
if (ret)
goto err_enable_clk;
/* /*
* According to the manual we should be able to let RTTR be zero * According to the manual we should be able to let RTTR be zero
* and then a default diviser for a 32.768KHz clock is used. * and then a default diviser for a 32.768KHz clock is used.
@ -305,6 +303,8 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
return 0; return 0;
err_dev: err_dev:
clk_disable_unprepare(info->clk);
err_enable_clk:
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
clk_put(info->clk); clk_put(info->clk);
err_clk: err_clk:
@ -318,6 +318,7 @@ static int sa1100_rtc_remove(struct platform_device *pdev)
if (info) { if (info) {
rtc_device_unregister(info->rtc); rtc_device_unregister(info->rtc);
clk_disable_unprepare(info->clk);
clk_put(info->clk); clk_put(info->clk);
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
kfree(info); kfree(info);
@ -349,12 +350,14 @@ static const struct dev_pm_ops sa1100_rtc_pm_ops = {
}; };
#endif #endif
#ifdef CONFIG_OF
static struct of_device_id sa1100_rtc_dt_ids[] = { static struct of_device_id sa1100_rtc_dt_ids[] = {
{ .compatible = "mrvl,sa1100-rtc", }, { .compatible = "mrvl,sa1100-rtc", },
{ .compatible = "mrvl,mmp-rtc", }, { .compatible = "mrvl,mmp-rtc", },
{} {}
}; };
MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids); MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids);
#endif
static struct platform_driver sa1100_rtc_driver = { static struct platform_driver sa1100_rtc_driver = {
.probe = sa1100_rtc_probe, .probe = sa1100_rtc_probe,
@ -364,7 +367,7 @@ static struct platform_driver sa1100_rtc_driver = {
#ifdef CONFIG_PM #ifdef CONFIG_PM
.pm = &sa1100_rtc_pm_ops, .pm = &sa1100_rtc_pm_ops,
#endif #endif
.of_match_table = sa1100_rtc_dt_ids, .of_match_table = of_match_ptr(sa1100_rtc_dt_ids),
}, },
}; };

View file

@ -338,7 +338,7 @@ static struct platform_driver snvs_rtc_driver = {
.name = "snvs_rtc", .name = "snvs_rtc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &snvs_rtc_pm_ops, .pm = &snvs_rtc_pm_ops,
.of_match_table = snvs_dt_ids, .of_match_table = of_match_ptr(snvs_dt_ids),
}, },
.probe = snvs_rtc_probe, .probe = snvs_rtc_probe,
.remove = snvs_rtc_remove, .remove = snvs_rtc_remove,

View file

@ -26,6 +26,7 @@
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of.h>
#include <mach/common.h> #include <mach/common.h>
@ -280,7 +281,7 @@ static struct platform_driver stmp3xxx_rtcdrv = {
.driver = { .driver = {
.name = "stmp3xxx-rtc", .name = "stmp3xxx-rtc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = rtc_dt_ids, .of_match_table = of_match_ptr(rtc_dt_ids),
}, },
}; };

View file

@ -3,6 +3,8 @@
* Copyright (C) 2008 David S. Miller <davem@davemloft.net> * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/delay.h> #include <linux/delay.h>
@ -26,10 +28,10 @@ retry:
udelay(100); udelay(100);
goto retry; goto retry;
} }
printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); pr_warn("tod_get() timed out.\n");
return 0; return 0;
} }
printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); pr_warn("tod_get() not supported.\n");
return 0; return 0;
} }
@ -53,10 +55,10 @@ retry:
udelay(100); udelay(100);
goto retry; goto retry;
} }
printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); pr_warn("tod_set() timed out.\n");
return -EAGAIN; return -EAGAIN;
} }
printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); pr_warn("tod_set() not supported.\n");
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }

View file

@ -282,7 +282,8 @@ static int tps6586x_rtc_probe(struct platform_device *pdev)
goto fail_rtc_register; goto fail_rtc_register;
} }
ret = request_threaded_irq(rtc->irq, NULL, tps6586x_rtc_irq, ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
tps6586x_rtc_irq,
IRQF_ONESHOT | IRQF_EARLY_RESUME, IRQF_ONESHOT | IRQF_EARLY_RESUME,
dev_name(&pdev->dev), rtc); dev_name(&pdev->dev), rtc);
if (ret < 0) { if (ret < 0) {
@ -311,7 +312,6 @@ static int tps6586x_rtc_remove(struct platform_device *pdev)
tps6586x_update(tps_dev, RTC_CTRL, 0, tps6586x_update(tps_dev, RTC_CTRL, 0,
RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK); RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK);
rtc_device_unregister(rtc->rtc); rtc_device_unregister(rtc->rtc);
free_irq(rtc->irq, rtc);
return 0; return 0;
} }

View file

@ -22,13 +22,13 @@
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/mfd/tps65910.h> #include <linux/mfd/tps65910.h>
struct tps65910_rtc { struct tps65910_rtc {
struct rtc_device *rtc; struct rtc_device *rtc;
/* To store the list of enabled interrupts */ int irq;
u32 irqstat;
}; };
/* Total number of RTC registers needed to set time*/ /* Total number of RTC registers needed to set time*/
@ -267,13 +267,14 @@ static int tps65910_rtc_probe(struct platform_device *pdev)
} }
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
tps65910_rtc_interrupt, IRQF_TRIGGER_LOW, tps65910_rtc_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME,
dev_name(&pdev->dev), &pdev->dev); dev_name(&pdev->dev), &pdev->dev);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "IRQ is not free.\n"); dev_err(&pdev->dev, "IRQ is not free.\n");
return ret; return ret;
} }
device_init_wakeup(&pdev->dev, 1); tps_rtc->irq = irq;
device_set_wakeup_capable(&pdev->dev, 1);
tps_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, tps_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&tps65910_rtc_ops, THIS_MODULE); &tps65910_rtc_ops, THIS_MODULE);
@ -304,49 +305,36 @@ static int tps65910_rtc_remove(struct platform_device *pdev)
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int tps65910_rtc_suspend(struct device *dev) static int tps65910_rtc_suspend(struct device *dev)
{ {
struct tps65910 *tps = dev_get_drvdata(dev->parent); struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev);
u8 alarm = TPS65910_RTC_INTERRUPTS_IT_ALARM;
int ret;
/* Store current list of enabled interrupts*/ if (device_may_wakeup(dev))
ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, enable_irq_wake(tps_rtc->irq);
&tps->rtc->irqstat); return 0;
if (ret < 0)
return ret;
/* Enable RTC ALARM interrupt only */
return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, alarm);
} }
static int tps65910_rtc_resume(struct device *dev) static int tps65910_rtc_resume(struct device *dev)
{ {
struct tps65910 *tps = dev_get_drvdata(dev->parent); struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev);
/* Restore list of enabled interrupts before suspend */ if (device_may_wakeup(dev))
return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, disable_irq_wake(tps_rtc->irq);
tps->rtc->irqstat); return 0;
} }
#endif
static const struct dev_pm_ops tps65910_rtc_pm_ops = { static const struct dev_pm_ops tps65910_rtc_pm_ops = {
.suspend = tps65910_rtc_suspend, SET_SYSTEM_SLEEP_PM_OPS(tps65910_rtc_suspend, tps65910_rtc_resume)
.resume = tps65910_rtc_resume,
}; };
#define DEV_PM_OPS (&tps65910_rtc_pm_ops)
#else
#define DEV_PM_OPS NULL
#endif
static struct platform_driver tps65910_rtc_driver = { static struct platform_driver tps65910_rtc_driver = {
.probe = tps65910_rtc_probe, .probe = tps65910_rtc_probe,
.remove = tps65910_rtc_remove, .remove = tps65910_rtc_remove,
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "tps65910-rtc", .name = "tps65910-rtc",
.pm = DEV_PM_OPS, .pm = &tps65910_rtc_pm_ops,
}, },
}; };

349
drivers/rtc/rtc-tps80031.c Normal file
View file

@ -0,0 +1,349 @@
/*
* rtc-tps80031.c -- TI TPS80031/TPS80032 RTC driver
*
* RTC driver for TI TPS80031/TPS80032 Fully Integrated
* Power Management with Power Path and Battery Charger
*
* Copyright (c) 2012, NVIDIA Corporation.
*
* Author: Laxman Dewangan <ldewangan@nvidia.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 version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
* whether express or implied; 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/bcd.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mfd/tps80031.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#define ENABLE_ALARM_INT 0x08
#define ALARM_INT_STATUS 0x40
/**
* Setting bit to 1 in STOP_RTC will run the RTC and
* setting this bit to 0 will freeze RTC.
*/
#define STOP_RTC 0x1
/* Power on reset Values of RTC registers */
#define TPS80031_RTC_POR_YEAR 0
#define TPS80031_RTC_POR_MONTH 1
#define TPS80031_RTC_POR_DAY 1
/* Numbers of registers for time and alarms */
#define TPS80031_RTC_TIME_NUM_REGS 7
#define TPS80031_RTC_ALARM_NUM_REGS 6
/**
* PMU RTC have only 2 nibbles to store year information, so using an
* offset of 100 to set the base year as 2000 for our driver.
*/
#define RTC_YEAR_OFFSET 100
struct tps80031_rtc {
struct rtc_device *rtc;
int irq;
};
static int tps80031_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
u8 buff[TPS80031_RTC_TIME_NUM_REGS];
int ret;
ret = tps80031_reads(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_SECONDS_REG, TPS80031_RTC_TIME_NUM_REGS, buff);
if (ret < 0) {
dev_err(dev, "reading RTC_SECONDS_REG failed, err = %d\n", ret);
return ret;
}
tm->tm_sec = bcd2bin(buff[0]);
tm->tm_min = bcd2bin(buff[1]);
tm->tm_hour = bcd2bin(buff[2]);
tm->tm_mday = bcd2bin(buff[3]);
tm->tm_mon = bcd2bin(buff[4]) - 1;
tm->tm_year = bcd2bin(buff[5]) + RTC_YEAR_OFFSET;
tm->tm_wday = bcd2bin(buff[6]);
return 0;
}
static int tps80031_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
u8 buff[7];
int ret;
buff[0] = bin2bcd(tm->tm_sec);
buff[1] = bin2bcd(tm->tm_min);
buff[2] = bin2bcd(tm->tm_hour);
buff[3] = bin2bcd(tm->tm_mday);
buff[4] = bin2bcd(tm->tm_mon + 1);
buff[5] = bin2bcd(tm->tm_year % RTC_YEAR_OFFSET);
buff[6] = bin2bcd(tm->tm_wday);
/* Stop RTC while updating the RTC time registers */
ret = tps80031_clr_bits(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_RTC_CTRL_REG, STOP_RTC);
if (ret < 0) {
dev_err(dev->parent, "Stop RTC failed, err = %d\n", ret);
return ret;
}
ret = tps80031_writes(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_SECONDS_REG,
TPS80031_RTC_TIME_NUM_REGS, buff);
if (ret < 0) {
dev_err(dev, "writing RTC_SECONDS_REG failed, err %d\n", ret);
return ret;
}
ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_RTC_CTRL_REG, STOP_RTC);
if (ret < 0)
dev_err(dev->parent, "Start RTC failed, err = %d\n", ret);
return ret;
}
static int tps80031_rtc_alarm_irq_enable(struct device *dev,
unsigned int enable)
{
int ret;
if (enable)
ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_RTC_INTERRUPTS_REG, ENABLE_ALARM_INT);
else
ret = tps80031_clr_bits(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_RTC_INTERRUPTS_REG, ENABLE_ALARM_INT);
if (ret < 0) {
dev_err(dev, "Update on RTC_INT failed, err = %d\n", ret);
return ret;
}
return 0;
}
static int tps80031_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
u8 buff[TPS80031_RTC_ALARM_NUM_REGS];
int ret;
buff[0] = bin2bcd(alrm->time.tm_sec);
buff[1] = bin2bcd(alrm->time.tm_min);
buff[2] = bin2bcd(alrm->time.tm_hour);
buff[3] = bin2bcd(alrm->time.tm_mday);
buff[4] = bin2bcd(alrm->time.tm_mon + 1);
buff[5] = bin2bcd(alrm->time.tm_year % RTC_YEAR_OFFSET);
ret = tps80031_writes(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_ALARM_SECONDS_REG,
TPS80031_RTC_ALARM_NUM_REGS, buff);
if (ret < 0) {
dev_err(dev, "Writing RTC_ALARM failed, err %d\n", ret);
return ret;
}
return tps80031_rtc_alarm_irq_enable(dev, alrm->enabled);
}
static int tps80031_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
u8 buff[6];
int ret;
ret = tps80031_reads(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_ALARM_SECONDS_REG,
TPS80031_RTC_ALARM_NUM_REGS, buff);
if (ret < 0) {
dev_err(dev->parent,
"reading RTC_ALARM failed, err = %d\n", ret);
return ret;
}
alrm->time.tm_sec = bcd2bin(buff[0]);
alrm->time.tm_min = bcd2bin(buff[1]);
alrm->time.tm_hour = bcd2bin(buff[2]);
alrm->time.tm_mday = bcd2bin(buff[3]);
alrm->time.tm_mon = bcd2bin(buff[4]) - 1;
alrm->time.tm_year = bcd2bin(buff[5]) + RTC_YEAR_OFFSET;
return 0;
}
static int clear_alarm_int_status(struct device *dev, struct tps80031_rtc *rtc)
{
int ret;
u8 buf;
/**
* As per datasheet, A dummy read of this RTC_STATUS_REG register
* is necessary before each I2C read in order to update the status
* register value.
*/
ret = tps80031_read(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_RTC_STATUS_REG, &buf);
if (ret < 0) {
dev_err(dev, "reading RTC_STATUS failed. err = %d\n", ret);
return ret;
}
/* clear Alarm status bits.*/
ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1,
TPS80031_RTC_STATUS_REG, ALARM_INT_STATUS);
if (ret < 0) {
dev_err(dev, "clear Alarm INT failed, err = %d\n", ret);
return ret;
}
return 0;
}
static irqreturn_t tps80031_rtc_irq(int irq, void *data)
{
struct device *dev = data;
struct tps80031_rtc *rtc = dev_get_drvdata(dev);
int ret;
ret = clear_alarm_int_status(dev, rtc);
if (ret < 0)
return ret;
rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
return IRQ_HANDLED;
}
static const struct rtc_class_ops tps80031_rtc_ops = {
.read_time = tps80031_rtc_read_time,
.set_time = tps80031_rtc_set_time,
.set_alarm = tps80031_rtc_set_alarm,
.read_alarm = tps80031_rtc_read_alarm,
.alarm_irq_enable = tps80031_rtc_alarm_irq_enable,
};
static int tps80031_rtc_probe(struct platform_device *pdev)
{
struct tps80031_rtc *rtc;
struct rtc_time tm;
int ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
rtc->irq = platform_get_irq(pdev, 0);
platform_set_drvdata(pdev, rtc);
/* Start RTC */
ret = tps80031_set_bits(pdev->dev.parent, TPS80031_SLAVE_ID1,
TPS80031_RTC_CTRL_REG, STOP_RTC);
if (ret < 0) {
dev_err(&pdev->dev, "failed to start RTC. err = %d\n", ret);
return ret;
}
/* If RTC have POR values, set time 01:01:2000 */
tps80031_rtc_read_time(&pdev->dev, &tm);
if ((tm.tm_year == RTC_YEAR_OFFSET + TPS80031_RTC_POR_YEAR) &&
(tm.tm_mon == (TPS80031_RTC_POR_MONTH - 1)) &&
(tm.tm_mday == TPS80031_RTC_POR_DAY)) {
tm.tm_year = 2000;
tm.tm_mday = 1;
tm.tm_mon = 1;
ret = tps80031_rtc_set_time(&pdev->dev, &tm);
if (ret < 0) {
dev_err(&pdev->dev,
"RTC set time failed, err = %d\n", ret);
return ret;
}
}
/* Clear alarm intretupt status if it is there */
ret = clear_alarm_int_status(&pdev->dev, rtc);
if (ret < 0) {
dev_err(&pdev->dev, "Clear alarm int failed, err = %d\n", ret);
return ret;
}
rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&tps80031_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc->rtc)) {
ret = PTR_ERR(rtc->rtc);
dev_err(&pdev->dev, "RTC registration failed, err %d\n", ret);
return ret;
}
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
tps80031_rtc_irq,
IRQF_ONESHOT | IRQF_EARLY_RESUME,
dev_name(&pdev->dev), rtc);
if (ret < 0) {
dev_err(&pdev->dev, "request IRQ:%d failed, err = %d\n",
rtc->irq, ret);
rtc_device_unregister(rtc->rtc);
return ret;
}
device_set_wakeup_capable(&pdev->dev, 1);
return 0;
}
static int tps80031_rtc_remove(struct platform_device *pdev)
{
struct tps80031_rtc *rtc = platform_get_drvdata(pdev);
rtc_device_unregister(rtc->rtc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int tps80031_rtc_suspend(struct device *dev)
{
struct tps80031_rtc *rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
enable_irq_wake(rtc->irq);
return 0;
}
static int tps80031_rtc_resume(struct device *dev)
{
struct tps80031_rtc *rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
disable_irq_wake(rtc->irq);
return 0;
};
#endif
static const struct dev_pm_ops tps80031_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tps80031_rtc_suspend, tps80031_rtc_resume)
};
static struct platform_driver tps80031_rtc_driver = {
.driver = {
.name = "tps80031-rtc",
.owner = THIS_MODULE,
.pm = &tps80031_pm_ops,
},
.probe = tps80031_rtc_probe,
.remove = tps80031_rtc_remove,
};
module_platform_driver(tps80031_rtc_driver);
MODULE_ALIAS("platform:tps80031-rtc");
MODULE_DESCRIPTION("TI TPS80031/TPS80032 RTC driver");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_LICENSE("GPL v2");

View file

@ -27,6 +27,7 @@
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/i2c/twl.h> #include <linux/i2c/twl.h>
@ -588,11 +589,14 @@ static int twl_rtc_resume(struct platform_device *pdev)
#define twl_rtc_resume NULL #define twl_rtc_resume NULL
#endif #endif
#ifdef CONFIG_OF
static const struct of_device_id twl_rtc_of_match[] = { static const struct of_device_id twl_rtc_of_match[] = {
{.compatible = "ti,twl4030-rtc", }, {.compatible = "ti,twl4030-rtc", },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, twl_rtc_of_match); MODULE_DEVICE_TABLE(of, twl_rtc_of_match);
#endif
MODULE_ALIAS("platform:twl_rtc"); MODULE_ALIAS("platform:twl_rtc");
static struct platform_driver twl4030rtc_driver = { static struct platform_driver twl4030rtc_driver = {
@ -604,7 +608,7 @@ static struct platform_driver twl4030rtc_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "twl_rtc", .name = "twl_rtc",
.of_match_table = twl_rtc_of_match, .of_match_table = of_match_ptr(twl_rtc_of_match),
}, },
}; };

View file

@ -352,7 +352,7 @@ static int rtc_probe(struct platform_device *pdev)
disable_irq(aie_irq); disable_irq(aie_irq);
disable_irq(pie_irq); disable_irq(pie_irq);
printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series\n"); dev_info(&pdev->dev, "Real Time Clock of NEC VR4100 series\n");
return 0; return 0;

View file

@ -231,20 +231,21 @@ static int vt8500_rtc_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
vt8500_rtc->res = request_mem_region(vt8500_rtc->res->start, vt8500_rtc->res = devm_request_mem_region(&pdev->dev,
resource_size(vt8500_rtc->res), vt8500_rtc->res->start,
"vt8500-rtc"); resource_size(vt8500_rtc->res),
"vt8500-rtc");
if (vt8500_rtc->res == NULL) { if (vt8500_rtc->res == NULL) {
dev_err(&pdev->dev, "failed to request I/O memory\n"); dev_err(&pdev->dev, "failed to request I/O memory\n");
return -EBUSY; return -EBUSY;
} }
vt8500_rtc->regbase = ioremap(vt8500_rtc->res->start, vt8500_rtc->regbase = devm_ioremap(&pdev->dev, vt8500_rtc->res->start,
resource_size(vt8500_rtc->res)); resource_size(vt8500_rtc->res));
if (!vt8500_rtc->regbase) { if (!vt8500_rtc->regbase) {
dev_err(&pdev->dev, "Unable to map RTC I/O memory\n"); dev_err(&pdev->dev, "Unable to map RTC I/O memory\n");
ret = -EBUSY; ret = -EBUSY;
goto err_release; goto err_return;
} }
/* Enable RTC and set it to 24-hour mode */ /* Enable RTC and set it to 24-hour mode */
@ -257,11 +258,11 @@ static int vt8500_rtc_probe(struct platform_device *pdev)
ret = PTR_ERR(vt8500_rtc->rtc); ret = PTR_ERR(vt8500_rtc->rtc);
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Failed to register RTC device -> %d\n", ret); "Failed to register RTC device -> %d\n", ret);
goto err_unmap; goto err_return;
} }
ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0, ret = devm_request_irq(&pdev->dev, vt8500_rtc->irq_alarm,
"rtc alarm", vt8500_rtc); vt8500_rtc_irq, 0, "rtc alarm", vt8500_rtc);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "can't get irq %i, err %d\n", dev_err(&pdev->dev, "can't get irq %i, err %d\n",
vt8500_rtc->irq_alarm, ret); vt8500_rtc->irq_alarm, ret);
@ -272,11 +273,7 @@ static int vt8500_rtc_probe(struct platform_device *pdev)
err_unreg: err_unreg:
rtc_device_unregister(vt8500_rtc->rtc); rtc_device_unregister(vt8500_rtc->rtc);
err_unmap: err_return:
iounmap(vt8500_rtc->regbase);
err_release:
release_mem_region(vt8500_rtc->res->start,
resource_size(vt8500_rtc->res));
return ret; return ret;
} }
@ -284,15 +281,10 @@ static int vt8500_rtc_remove(struct platform_device *pdev)
{ {
struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev); struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
free_irq(vt8500_rtc->irq_alarm, vt8500_rtc);
rtc_device_unregister(vt8500_rtc->rtc); rtc_device_unregister(vt8500_rtc->rtc);
/* Disable alarm matching */ /* Disable alarm matching */
writel(0, vt8500_rtc->regbase + VT8500_RTC_IS); writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
iounmap(vt8500_rtc->regbase);
release_mem_region(vt8500_rtc->res->start,
resource_size(vt8500_rtc->res));
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);

View file

@ -443,9 +443,10 @@ static int wm831x_rtc_probe(struct platform_device *pdev)
goto err; goto err;
} }
ret = request_threaded_irq(alm_irq, NULL, wm831x_alm_irq, ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
IRQF_TRIGGER_RISING, "RTC alarm", wm831x_alm_irq,
wm831x_rtc); IRQF_TRIGGER_RISING, "RTC alarm",
wm831x_rtc);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
alm_irq, ret); alm_irq, ret);
@ -462,9 +463,7 @@ err:
static int wm831x_rtc_remove(struct platform_device *pdev) static int wm831x_rtc_remove(struct platform_device *pdev)
{ {
struct wm831x_rtc *wm831x_rtc = platform_get_drvdata(pdev); struct wm831x_rtc *wm831x_rtc = platform_get_drvdata(pdev);
int alm_irq = platform_get_irq_byname(pdev, "ALM");
free_irq(alm_irq, wm831x_rtc);
rtc_device_unregister(wm831x_rtc->rtc); rtc_device_unregister(wm831x_rtc->rtc);
return 0; return 0;

View file

@ -364,7 +364,7 @@ config FB_SA1100
Y here. Y here.
config FB_IMX config FB_IMX
tristate "Freescale i.MX LCD support" tristate "Freescale i.MX1/21/25/27 LCD support"
depends on FB && IMX_HAVE_PLATFORM_IMX_FB depends on FB && IMX_HAVE_PLATFORM_IMX_FB
select FB_CFB_FILLRECT select FB_CFB_FILLRECT
select FB_CFB_COPYAREA select FB_CFB_COPYAREA
@ -2025,7 +2025,8 @@ config FB_TMIO_ACCELL
config FB_S3C config FB_S3C
tristate "Samsung S3C framebuffer support" tristate "Samsung S3C framebuffer support"
depends on FB && (S3C_DEV_FB || S5P_DEV_FIMD0) depends on FB && (CPU_S3C2416 || ARCH_S3C64XX || ARCH_S5P64X0 || \
ARCH_S5PC100 || ARCH_S5PV210 || ARCH_EXYNOS)
select FB_CFB_FILLRECT select FB_CFB_FILLRECT
select FB_CFB_COPYAREA select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT select FB_CFB_IMAGEBLIT
@ -2183,6 +2184,15 @@ config FB_XILINX
framebuffer. ML300 carries a 640*480 LCD display on the board, framebuffer. ML300 carries a 640*480 LCD display on the board,
ML403 uses a standard DB15 VGA connector. ML403 uses a standard DB15 VGA connector.
config FB_GOLDFISH
tristate "Goldfish Framebuffer"
depends on FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
---help---
Framebuffer driver for Goldfish Virtual Platform
config FB_COBALT config FB_COBALT
tristate "Cobalt server LCD frame buffer support" tristate "Cobalt server LCD frame buffer support"
depends on FB && (MIPS_COBALT || MIPS_SEAD3) depends on FB && (MIPS_COBALT || MIPS_SEAD3)
@ -2422,6 +2432,7 @@ config FB_PUV3_UNIGFX
source "drivers/video/omap/Kconfig" source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig" source "drivers/video/omap2/Kconfig"
source "drivers/video/exynos/Kconfig" source "drivers/video/exynos/Kconfig"
source "drivers/video/mmp/Kconfig"
source "drivers/video/backlight/Kconfig" source "drivers/video/backlight/Kconfig"
if VT if VT

View file

@ -98,6 +98,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o
obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o
obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o
obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o
obj-$(CONFIG_FB_GOLDFISH) += goldfishfb.o
obj-$(CONFIG_FB_68328) += 68328fb.o obj-$(CONFIG_FB_68328) += 68328fb.o
obj-$(CONFIG_FB_GBE) += gbefb.o obj-$(CONFIG_FB_GBE) += gbefb.o
obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o
@ -105,6 +106,7 @@ obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o
obj-$(CONFIG_FB_PXA) += pxafb.o obj-$(CONFIG_FB_PXA) += pxafb.o
obj-$(CONFIG_FB_PXA168) += pxa168fb.o obj-$(CONFIG_FB_PXA168) += pxa168fb.o
obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o obj-$(CONFIG_PXA3XX_GCU) += pxa3xx-gcu.o
obj-$(CONFIG_MMP_DISP) += mmp/
obj-$(CONFIG_FB_W100) += w100fb.o obj-$(CONFIG_FB_W100) += w100fb.o
obj-$(CONFIG_FB_TMIO) += tmiofb.o obj-$(CONFIG_FB_TMIO) += tmiofb.o
obj-$(CONFIG_FB_AU1100) += au1100fb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o

View file

@ -165,8 +165,10 @@ static int pm860x_backlight_dt_init(struct platform_device *pdev,
struct pm860x_backlight_data *data, struct pm860x_backlight_data *data,
char *name) char *name)
{ {
struct device_node *nproot = pdev->dev.parent->of_node, *np; struct device_node *nproot, *np;
int iset = 0; int iset = 0;
nproot = of_node_get(pdev->dev.parent->of_node);
if (!nproot) if (!nproot)
return -ENODEV; return -ENODEV;
nproot = of_find_node_by_name(nproot, "backlights"); nproot = of_find_node_by_name(nproot, "backlights");
@ -184,6 +186,7 @@ static int pm860x_backlight_dt_init(struct platform_device *pdev,
break; break;
} }
} }
of_node_put(nproot);
return 0; return 0;
} }
#else #else

View file

@ -126,6 +126,21 @@ config LCD_AMS369FG06
If you have an AMS369FG06 AMOLED Panel, say Y to enable its If you have an AMS369FG06 AMOLED Panel, say Y to enable its
LCD control driver. LCD control driver.
config LCD_LMS501KF03
tristate "LMS501KF03 LCD Driver"
depends on SPI
default n
help
If you have an LMS501KF03 LCD Panel, say Y to enable its
LCD control driver.
config LCD_HX8357
tristate "Himax HX-8357 LCD Driver"
depends on SPI
help
If you have a HX-8357 LCD panel, say Y to enable its LCD control
driver.
endif # LCD_CLASS_DEVICE endif # LCD_CLASS_DEVICE
# #
@ -366,7 +381,7 @@ config BACKLIGHT_LP855X
tristate "Backlight driver for TI LP855X" tristate "Backlight driver for TI LP855X"
depends on BACKLIGHT_CLASS_DEVICE && I2C depends on BACKLIGHT_CLASS_DEVICE && I2C
help help
This supports TI LP8550, LP8551, LP8552, LP8553 and LP8556 This supports TI LP8550, LP8551, LP8552, LP8553, LP8556 and LP8557
backlight driver. backlight driver.
config BACKLIGHT_OT200 config BACKLIGHT_OT200
@ -390,6 +405,13 @@ config BACKLIGHT_TPS65217
If you have a Texas Instruments TPS65217 say Y to enable the If you have a Texas Instruments TPS65217 say Y to enable the
backlight driver. backlight driver.
config BACKLIGHT_AS3711
tristate "AS3711 Backlight"
depends on BACKLIGHT_CLASS_DEVICE && MFD_AS3711
help
If you have an Austrian Microsystems AS3711 say Y to enable the
backlight driver.
endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_CLASS_DEVICE
endif # BACKLIGHT_LCD_SUPPORT endif # BACKLIGHT_LCD_SUPPORT

View file

@ -1,47 +1,50 @@
# Backlight & LCD drivers # Backlight & LCD drivers
obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o
obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o
obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o
obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o obj-$(CONFIG_LCD_HX8357) += hx8357.o
obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o obj-$(CONFIG_LCD_ILI9320) += ili9320.o
obj-$(CONFIG_LCD_ILI9320) += ili9320.o obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o
obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o obj-$(CONFIG_LCD_LD9040) += ld9040.o
obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o
obj-$(CONFIG_LCD_TDO24M) += tdo24m.o obj-$(CONFIG_LCD_LMS501KF03) += lms501kf03.o
obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o
obj-$(CONFIG_LCD_LD9040) += ld9040.o obj-$(CONFIG_LCD_S6E63M0) += s6e63m0.o
obj-$(CONFIG_LCD_AMS369FG06) += ams369fg06.o obj-$(CONFIG_LCD_TDO24M) += tdo24m.o
obj-$(CONFIG_LCD_TOSA) += tosa_lcd.o
obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o
obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o
obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o
obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o
obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o obj-$(CONFIG_BACKLIGHT_AS3711) += as3711_bl.o
obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o obj-$(CONFIG_BACKLIGHT_ATMEL_PWM) += atmel-pwm-bl.o
obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o
obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o
obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o obj-$(CONFIG_BACKLIGHT_EP93XX) += ep93xx_bl.o
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o
obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
obj-$(CONFIG_BACKLIGHT_DA903X) += da903x_bl.o obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o
obj-$(CONFIG_BACKLIGHT_DA9052) += da9052_bl.o obj-$(CONFIG_BACKLIGHT_LM3533) += lm3533_bl.o
obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o obj-$(CONFIG_BACKLIGHT_LM3630) += lm3630_bl.o
obj-$(CONFIG_BACKLIGHT_APPLE) += apple_bl.o obj-$(CONFIG_BACKLIGHT_LM3639) += lm3639_bl.o
obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o obj-$(CONFIG_BACKLIGHT_MAX8925) += max8925_bl.o
obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o
obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o
obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o
obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o obj-$(CONFIG_BACKLIGHT_SAHARA) += kb3886_bl.o
obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o
obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o
obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o

View file

@ -74,7 +74,7 @@ static int aat2870_bl_get_brightness(struct backlight_device *bd)
static int aat2870_bl_update_status(struct backlight_device *bd) static int aat2870_bl_update_status(struct backlight_device *bd)
{ {
struct aat2870_bl_driver_data *aat2870_bl = dev_get_drvdata(&bd->dev); struct aat2870_bl_driver_data *aat2870_bl = bl_get_data(bd);
struct aat2870_data *aat2870 = struct aat2870_data *aat2870 =
dev_get_drvdata(aat2870_bl->pdev->dev.parent); dev_get_drvdata(aat2870_bl->pdev->dev.parent);
int brightness = bd->props.brightness; int brightness = bd->props.brightness;

View file

@ -783,7 +783,7 @@ static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message)
static int adp8860_i2c_resume(struct i2c_client *client) static int adp8860_i2c_resume(struct i2c_client *client)
{ {
adp8860_set_bits(client, ADP8860_MDCR, NSTBY); adp8860_set_bits(client, ADP8860_MDCR, NSTBY | BLEN);
return 0; return 0;
} }

View file

@ -957,7 +957,7 @@ static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message)
static int adp8870_i2c_resume(struct i2c_client *client) static int adp8870_i2c_resume(struct i2c_client *client)
{ {
adp8870_set_bits(client, ADP8870_MDCR, NSTBY); adp8870_set_bits(client, ADP8870_MDCR, NSTBY | BLEN);
return 0; return 0;
} }

View file

@ -10,25 +10,16 @@
* under the terms of the GNU General Public License as published by the * 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 * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * 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/wait.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/lcd.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/lcd.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/wait.h>
#define SLEEPMSEC 0x1000 #define SLEEPMSEC 0x1000
#define ENDDEF 0x2000 #define ENDDEF 0x2000
@ -210,8 +201,9 @@ static int ams369fg06_panel_send_sequence(struct ams369fg06 *lcd,
ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]); ret = ams369fg06_spi_write(lcd, wbuf[i], wbuf[i+1]);
if (ret) if (ret)
break; break;
} else } else {
mdelay(wbuf[i+1]); msleep(wbuf[i+1]);
}
i += 2; i += 2;
} }
@ -313,41 +305,32 @@ static int ams369fg06_ldi_disable(struct ams369fg06 *lcd)
static int ams369fg06_power_is_on(int power) static int ams369fg06_power_is_on(int power)
{ {
return ((power) <= FB_BLANK_NORMAL); return power <= FB_BLANK_NORMAL;
} }
static int ams369fg06_power_on(struct ams369fg06 *lcd) static int ams369fg06_power_on(struct ams369fg06 *lcd)
{ {
int ret = 0; int ret = 0;
struct lcd_platform_data *pd = NULL; struct lcd_platform_data *pd;
struct backlight_device *bd = NULL; struct backlight_device *bd;
pd = lcd->lcd_pd; pd = lcd->lcd_pd;
if (!pd) {
dev_err(lcd->dev, "platform data is NULL.\n");
return -EFAULT;
}
bd = lcd->bd; bd = lcd->bd;
if (!bd) {
dev_err(lcd->dev, "backlight device is NULL.\n");
return -EFAULT;
}
if (!pd->power_on) { if (!pd->power_on) {
dev_err(lcd->dev, "power_on is NULL.\n"); dev_err(lcd->dev, "power_on is NULL.\n");
return -EFAULT; return -EINVAL;
} else { } else {
pd->power_on(lcd->ld, 1); pd->power_on(lcd->ld, 1);
mdelay(pd->power_on_delay); msleep(pd->power_on_delay);
} }
if (!pd->reset) { if (!pd->reset) {
dev_err(lcd->dev, "reset is NULL.\n"); dev_err(lcd->dev, "reset is NULL.\n");
return -EFAULT; return -EINVAL;
} else { } else {
pd->reset(lcd->ld); pd->reset(lcd->ld);
mdelay(pd->reset_delay); msleep(pd->reset_delay);
} }
ret = ams369fg06_ldi_init(lcd); ret = ams369fg06_ldi_init(lcd);
@ -374,14 +357,10 @@ static int ams369fg06_power_on(struct ams369fg06 *lcd)
static int ams369fg06_power_off(struct ams369fg06 *lcd) static int ams369fg06_power_off(struct ams369fg06 *lcd)
{ {
int ret = 0; int ret;
struct lcd_platform_data *pd = NULL; struct lcd_platform_data *pd;
pd = lcd->lcd_pd; pd = lcd->lcd_pd;
if (!pd) {
dev_err(lcd->dev, "platform data is NULL\n");
return -EFAULT;
}
ret = ams369fg06_ldi_disable(lcd); ret = ams369fg06_ldi_disable(lcd);
if (ret) { if (ret) {
@ -389,13 +368,9 @@ static int ams369fg06_power_off(struct ams369fg06 *lcd)
return -EIO; return -EIO;
} }
mdelay(pd->power_off_delay); msleep(pd->power_off_delay);
if (!pd->power_on) { pd->power_on(lcd->ld, 0);
dev_err(lcd->dev, "power_on is NULL.\n");
return -EFAULT;
} else
pd->power_on(lcd->ld, 0);
return 0; return 0;
} }
@ -446,7 +421,7 @@ static int ams369fg06_set_brightness(struct backlight_device *bd)
{ {
int ret = 0; int ret = 0;
int brightness = bd->props.brightness; int brightness = bd->props.brightness;
struct ams369fg06 *lcd = dev_get_drvdata(&bd->dev); struct ams369fg06 *lcd = bl_get_data(bd);
if (brightness < MIN_BRIGHTNESS || if (brightness < MIN_BRIGHTNESS ||
brightness > bd->props.max_brightness) { brightness > bd->props.max_brightness) {
@ -501,7 +476,7 @@ static int ams369fg06_probe(struct spi_device *spi)
lcd->lcd_pd = spi->dev.platform_data; lcd->lcd_pd = spi->dev.platform_data;
if (!lcd->lcd_pd) { if (!lcd->lcd_pd) {
dev_err(&spi->dev, "platform data is NULL\n"); dev_err(&spi->dev, "platform data is NULL\n");
return -EFAULT; return -EINVAL;
} }
ld = lcd_device_register("ams369fg06", &spi->dev, lcd, ld = lcd_device_register("ams369fg06", &spi->dev, lcd,
@ -534,10 +509,11 @@ static int ams369fg06_probe(struct spi_device *spi)
lcd->power = FB_BLANK_POWERDOWN; lcd->power = FB_BLANK_POWERDOWN;
ams369fg06_power(lcd, FB_BLANK_UNBLANK); ams369fg06_power(lcd, FB_BLANK_UNBLANK);
} else } else {
lcd->power = FB_BLANK_UNBLANK; lcd->power = FB_BLANK_UNBLANK;
}
dev_set_drvdata(&spi->dev, lcd); spi_set_drvdata(spi, lcd);
dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n"); dev_info(&spi->dev, "ams369fg06 panel driver has been probed.\n");
@ -550,7 +526,7 @@ out_lcd_unregister:
static int ams369fg06_remove(struct spi_device *spi) static int ams369fg06_remove(struct spi_device *spi)
{ {
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); struct ams369fg06 *lcd = spi_get_drvdata(spi);
ams369fg06_power(lcd, FB_BLANK_POWERDOWN); ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
backlight_device_unregister(lcd->bd); backlight_device_unregister(lcd->bd);
@ -560,44 +536,26 @@ static int ams369fg06_remove(struct spi_device *spi)
} }
#if defined(CONFIG_PM) #if defined(CONFIG_PM)
static unsigned int before_power;
static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg) static int ams369fg06_suspend(struct spi_device *spi, pm_message_t mesg)
{ {
int ret = 0; struct ams369fg06 *lcd = spi_get_drvdata(spi);
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
before_power = lcd->power;
/* /*
* when lcd panel is suspend, lcd panel becomes off * when lcd panel is suspend, lcd panel becomes off
* regardless of status. * regardless of status.
*/ */
ret = ams369fg06_power(lcd, FB_BLANK_POWERDOWN); return ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
return ret;
} }
static int ams369fg06_resume(struct spi_device *spi) static int ams369fg06_resume(struct spi_device *spi)
{ {
int ret = 0; struct ams369fg06 *lcd = spi_get_drvdata(spi);
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev);
/* lcd->power = FB_BLANK_POWERDOWN;
* after suspended, if lcd panel status is FB_BLANK_UNBLANK
* (at that time, before_power is FB_BLANK_UNBLANK) then
* it changes that status to FB_BLANK_POWERDOWN to get lcd on.
*/
if (before_power == FB_BLANK_UNBLANK)
lcd->power = FB_BLANK_POWERDOWN;
dev_dbg(&spi->dev, "before_power = %d\n", before_power); return ams369fg06_power(lcd, FB_BLANK_UNBLANK);
ret = ams369fg06_power(lcd, before_power);
return ret;
} }
#else #else
#define ams369fg06_suspend NULL #define ams369fg06_suspend NULL
@ -606,7 +564,7 @@ static int ams369fg06_resume(struct spi_device *spi)
static void ams369fg06_shutdown(struct spi_device *spi) static void ams369fg06_shutdown(struct spi_device *spi)
{ {
struct ams369fg06 *lcd = dev_get_drvdata(&spi->dev); struct ams369fg06 *lcd = spi_get_drvdata(spi);
ams369fg06_power(lcd, FB_BLANK_POWERDOWN); ams369fg06_power(lcd, FB_BLANK_POWERDOWN);
} }

View file

@ -0,0 +1,380 @@
/*
* AS3711 PMIC backlight driver, using DCDC Step Up Converters
*
* Copyright (C) 2012 Renesas Electronics Corporation
* Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the version 2 of the GNU General Public License as
* published by the Free Software Foundation
*/
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fb.h>
#include <linux/kernel.h>
#include <linux/mfd/as3711.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
enum as3711_bl_type {
AS3711_BL_SU1,
AS3711_BL_SU2,
};
struct as3711_bl_data {
bool powered;
const char *fb_name;
struct device *fb_dev;
enum as3711_bl_type type;
int brightness;
struct backlight_device *bl;
};
struct as3711_bl_supply {
struct as3711_bl_data su1;
struct as3711_bl_data su2;
const struct as3711_bl_pdata *pdata;
struct as3711 *as3711;
};
static struct as3711_bl_supply *to_supply(struct as3711_bl_data *su)
{
switch (su->type) {
case AS3711_BL_SU1:
return container_of(su, struct as3711_bl_supply, su1);
case AS3711_BL_SU2:
return container_of(su, struct as3711_bl_supply, su2);
}
return NULL;
}
static int as3711_set_brightness_auto_i(struct as3711_bl_data *data,
unsigned int brightness)
{
struct as3711_bl_supply *supply = to_supply(data);
struct as3711 *as3711 = supply->as3711;
const struct as3711_bl_pdata *pdata = supply->pdata;
int ret = 0;
/* Only all equal current values are supported */
if (pdata->su2_auto_curr1)
ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
brightness);
if (!ret && pdata->su2_auto_curr2)
ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
brightness);
if (!ret && pdata->su2_auto_curr3)
ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
brightness);
return ret;
}
static int as3711_set_brightness_v(struct as3711 *as3711,
unsigned int brightness,
unsigned int reg)
{
if (brightness > 31)
return -EINVAL;
return regmap_update_bits(as3711->regmap, reg, 0xf0,
brightness << 4);
}
static int as3711_bl_su2_reset(struct as3711_bl_supply *supply)
{
struct as3711 *as3711 = supply->as3711;
int ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_5,
3, supply->pdata->su2_fbprot);
if (!ret)
ret = regmap_update_bits(as3711->regmap,
AS3711_STEPUP_CONTROL_2, 1, 0);
if (!ret)
ret = regmap_update_bits(as3711->regmap,
AS3711_STEPUP_CONTROL_2, 1, 1);
return ret;
}
/*
* Someone with less fragile or less expensive hardware could try to simplify
* the brightness adjustment procedure.
*/
static int as3711_bl_update_status(struct backlight_device *bl)
{
struct as3711_bl_data *data = bl_get_data(bl);
struct as3711_bl_supply *supply = to_supply(data);
struct as3711 *as3711 = supply->as3711;
int brightness = bl->props.brightness;
int ret = 0;
dev_dbg(&bl->dev, "%s(): brightness %u, pwr %x, blank %x, state %x\n",
__func__, bl->props.brightness, bl->props.power,
bl->props.fb_blank, bl->props.state);
if (bl->props.power != FB_BLANK_UNBLANK ||
bl->props.fb_blank != FB_BLANK_UNBLANK ||
bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
if (data->type == AS3711_BL_SU1) {
ret = as3711_set_brightness_v(as3711, brightness,
AS3711_STEPUP_CONTROL_1);
} else {
const struct as3711_bl_pdata *pdata = supply->pdata;
switch (pdata->su2_feedback) {
case AS3711_SU2_VOLTAGE:
ret = as3711_set_brightness_v(as3711, brightness,
AS3711_STEPUP_CONTROL_2);
break;
case AS3711_SU2_CURR_AUTO:
ret = as3711_set_brightness_auto_i(data, brightness / 4);
if (ret < 0)
return ret;
if (brightness) {
ret = as3711_bl_su2_reset(supply);
if (ret < 0)
return ret;
udelay(500);
ret = as3711_set_brightness_auto_i(data, brightness);
} else {
ret = regmap_update_bits(as3711->regmap,
AS3711_STEPUP_CONTROL_2, 1, 0);
}
break;
/* Manual one current feedback pin below */
case AS3711_SU2_CURR1:
ret = regmap_write(as3711->regmap, AS3711_CURR1_VALUE,
brightness);
break;
case AS3711_SU2_CURR2:
ret = regmap_write(as3711->regmap, AS3711_CURR2_VALUE,
brightness);
break;
case AS3711_SU2_CURR3:
ret = regmap_write(as3711->regmap, AS3711_CURR3_VALUE,
brightness);
break;
default:
ret = -EINVAL;
}
}
if (!ret)
data->brightness = brightness;
return ret;
}
static int as3711_bl_get_brightness(struct backlight_device *bl)
{
struct as3711_bl_data *data = bl_get_data(bl);
return data->brightness;
}
static const struct backlight_ops as3711_bl_ops = {
.update_status = as3711_bl_update_status,
.get_brightness = as3711_bl_get_brightness,
};
static int as3711_bl_init_su2(struct as3711_bl_supply *supply)
{
struct as3711 *as3711 = supply->as3711;
const struct as3711_bl_pdata *pdata = supply->pdata;
u8 ctl = 0;
int ret;
dev_dbg(as3711->dev, "%s(): use %u\n", __func__, pdata->su2_feedback);
/* Turn SU2 off */
ret = regmap_write(as3711->regmap, AS3711_STEPUP_CONTROL_2, 0);
if (ret < 0)
return ret;
switch (pdata->su2_feedback) {
case AS3711_SU2_VOLTAGE:
ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 0);
break;
case AS3711_SU2_CURR1:
ctl = 1;
ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 1);
break;
case AS3711_SU2_CURR2:
ctl = 4;
ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 2);
break;
case AS3711_SU2_CURR3:
ctl = 0x10;
ret = regmap_update_bits(as3711->regmap, AS3711_STEPUP_CONTROL_4, 3, 3);
break;
case AS3711_SU2_CURR_AUTO:
if (pdata->su2_auto_curr1)
ctl = 2;
if (pdata->su2_auto_curr2)
ctl |= 8;
if (pdata->su2_auto_curr3)
ctl |= 0x20;
ret = 0;
break;
default:
return -EINVAL;
}
if (!ret)
ret = regmap_write(as3711->regmap, AS3711_CURR_CONTROL, ctl);
return ret;
}
static int as3711_bl_register(struct platform_device *pdev,
unsigned int max_brightness, struct as3711_bl_data *su)
{
struct backlight_properties props = {.type = BACKLIGHT_RAW,};
struct backlight_device *bl;
/* max tuning I = 31uA for voltage- and 38250uA for current-feedback */
props.max_brightness = max_brightness;
bl = backlight_device_register(su->type == AS3711_BL_SU1 ?
"as3711-su1" : "as3711-su2",
&pdev->dev, su,
&as3711_bl_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
return PTR_ERR(bl);
}
bl->props.brightness = props.max_brightness;
backlight_update_status(bl);
su->bl = bl;
return 0;
}
static int as3711_backlight_probe(struct platform_device *pdev)
{
struct as3711_bl_pdata *pdata = dev_get_platdata(&pdev->dev);
struct as3711 *as3711 = dev_get_drvdata(pdev->dev.parent);
struct as3711_bl_supply *supply;
struct as3711_bl_data *su;
unsigned int max_brightness;
int ret;
if (!pdata || (!pdata->su1_fb && !pdata->su2_fb)) {
dev_err(&pdev->dev, "No platform data, exiting...\n");
return -ENODEV;
}
/*
* Due to possible hardware damage I chose to block all modes,
* unsupported on my hardware. Anyone, wishing to use any of those modes
* will have to first review the code, then activate and test it.
*/
if (pdata->su1_fb ||
pdata->su2_fbprot != AS3711_SU2_GPIO4 ||
pdata->su2_feedback != AS3711_SU2_CURR_AUTO) {
dev_warn(&pdev->dev,
"Attention! An untested mode has been chosen!\n"
"Please, review the code, enable, test, and report success:-)\n");
return -EINVAL;
}
supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL);
if (!supply)
return -ENOMEM;
supply->as3711 = as3711;
supply->pdata = pdata;
if (pdata->su1_fb) {
su = &supply->su1;
su->fb_name = pdata->su1_fb;
su->type = AS3711_BL_SU1;
max_brightness = min(pdata->su1_max_uA, 31);
ret = as3711_bl_register(pdev, max_brightness, su);
if (ret < 0)
return ret;
}
if (pdata->su2_fb) {
su = &supply->su2;
su->fb_name = pdata->su2_fb;
su->type = AS3711_BL_SU2;
switch (pdata->su2_fbprot) {
case AS3711_SU2_GPIO2:
case AS3711_SU2_GPIO3:
case AS3711_SU2_GPIO4:
case AS3711_SU2_LX_SD4:
break;
default:
ret = -EINVAL;
goto esu2;
}
switch (pdata->su2_feedback) {
case AS3711_SU2_VOLTAGE:
max_brightness = min(pdata->su2_max_uA, 31);
break;
case AS3711_SU2_CURR1:
case AS3711_SU2_CURR2:
case AS3711_SU2_CURR3:
case AS3711_SU2_CURR_AUTO:
max_brightness = min(pdata->su2_max_uA / 150, 255);
break;
default:
ret = -EINVAL;
goto esu2;
}
ret = as3711_bl_init_su2(supply);
if (ret < 0)
return ret;
ret = as3711_bl_register(pdev, max_brightness, su);
if (ret < 0)
goto esu2;
}
platform_set_drvdata(pdev, supply);
return 0;
esu2:
backlight_device_unregister(supply->su1.bl);
return ret;
}
static int as3711_backlight_remove(struct platform_device *pdev)
{
struct as3711_bl_supply *supply = platform_get_drvdata(pdev);
backlight_device_unregister(supply->su1.bl);
backlight_device_unregister(supply->su2.bl);
return 0;
}
static struct platform_driver as3711_backlight_driver = {
.driver = {
.name = "as3711-backlight",
.owner = THIS_MODULE,
},
.probe = as3711_backlight_probe,
.remove = as3711_backlight_remove,
};
module_platform_driver(as3711_backlight_driver);
MODULE_DESCRIPTION("Backlight Driver for AS3711 PMICs");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:as3711-backlight");

View file

@ -337,7 +337,7 @@ static void corgi_lcd_power_off(struct corgi_lcd *lcd)
static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m) static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); struct corgi_lcd *lcd = lcd_get_data(ld);
int mode = CORGI_LCD_MODE_QVGA; int mode = CORGI_LCD_MODE_QVGA;
if (m->xres == 640 || m->xres == 480) if (m->xres == 640 || m->xres == 480)
@ -364,7 +364,7 @@ static int corgi_lcd_set_mode(struct lcd_device *ld, struct fb_videomode *m)
static int corgi_lcd_set_power(struct lcd_device *ld, int power) static int corgi_lcd_set_power(struct lcd_device *ld, int power)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); struct corgi_lcd *lcd = lcd_get_data(ld);
if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power))
corgi_lcd_power_on(lcd); corgi_lcd_power_on(lcd);
@ -378,7 +378,7 @@ static int corgi_lcd_set_power(struct lcd_device *ld, int power)
static int corgi_lcd_get_power(struct lcd_device *ld) static int corgi_lcd_get_power(struct lcd_device *ld)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&ld->dev); struct corgi_lcd *lcd = lcd_get_data(ld);
return lcd->power; return lcd->power;
} }
@ -391,7 +391,7 @@ static struct lcd_ops corgi_lcd_ops = {
static int corgi_bl_get_intensity(struct backlight_device *bd) static int corgi_bl_get_intensity(struct backlight_device *bd)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); struct corgi_lcd *lcd = bl_get_data(bd);
return lcd->intensity; return lcd->intensity;
} }
@ -423,7 +423,7 @@ static int corgi_bl_set_intensity(struct corgi_lcd *lcd, int intensity)
static int corgi_bl_update_status(struct backlight_device *bd) static int corgi_bl_update_status(struct backlight_device *bd)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&bd->dev); struct corgi_lcd *lcd = bl_get_data(bd);
int intensity = bd->props.brightness; int intensity = bd->props.brightness;
if (bd->props.power != FB_BLANK_UNBLANK) if (bd->props.power != FB_BLANK_UNBLANK)
@ -460,7 +460,7 @@ static const struct backlight_ops corgi_bl_ops = {
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state) static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); struct corgi_lcd *lcd = spi_get_drvdata(spi);
corgibl_flags |= CORGIBL_SUSPENDED; corgibl_flags |= CORGIBL_SUSPENDED;
corgi_bl_set_intensity(lcd, 0); corgi_bl_set_intensity(lcd, 0);
@ -470,7 +470,7 @@ static int corgi_lcd_suspend(struct spi_device *spi, pm_message_t state)
static int corgi_lcd_resume(struct spi_device *spi) static int corgi_lcd_resume(struct spi_device *spi)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); struct corgi_lcd *lcd = spi_get_drvdata(spi);
corgibl_flags &= ~CORGIBL_SUSPENDED; corgibl_flags &= ~CORGIBL_SUSPENDED;
corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
@ -577,7 +577,7 @@ static int corgi_lcd_probe(struct spi_device *spi)
lcd->kick_battery = pdata->kick_battery; lcd->kick_battery = pdata->kick_battery;
dev_set_drvdata(&spi->dev, lcd); spi_set_drvdata(spi, lcd);
corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK); corgi_lcd_set_power(lcd->lcd_dev, FB_BLANK_UNBLANK);
backlight_update_status(lcd->bl_dev); backlight_update_status(lcd->bl_dev);
@ -594,7 +594,7 @@ err_unregister_lcd:
static int corgi_lcd_remove(struct spi_device *spi) static int corgi_lcd_remove(struct spi_device *spi)
{ {
struct corgi_lcd *lcd = dev_get_drvdata(&spi->dev); struct corgi_lcd *lcd = spi_get_drvdata(spi);
lcd->bl_dev->props.power = FB_BLANK_UNBLANK; lcd->bl_dev->props.power = FB_BLANK_UNBLANK;
lcd->bl_dev->props.brightness = 0; lcd->bl_dev->props.brightness = 0;

View file

@ -0,0 +1,497 @@
/*
* Driver for the Himax HX-8357 LCD Controller
*
* Copyright 2012 Free Electrons
*
* Licensed under the GPLv2 or later.
*/
#include <linux/delay.h>
#include <linux/lcd.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/spi/spi.h>
#define HX8357_NUM_IM_PINS 3
#define HX8357_SWRESET 0x01
#define HX8357_GET_RED_CHANNEL 0x06
#define HX8357_GET_GREEN_CHANNEL 0x07
#define HX8357_GET_BLUE_CHANNEL 0x08
#define HX8357_GET_POWER_MODE 0x0a
#define HX8357_GET_MADCTL 0x0b
#define HX8357_GET_PIXEL_FORMAT 0x0c
#define HX8357_GET_DISPLAY_MODE 0x0d
#define HX8357_GET_SIGNAL_MODE 0x0e
#define HX8357_GET_DIAGNOSTIC_RESULT 0x0f
#define HX8357_ENTER_SLEEP_MODE 0x10
#define HX8357_EXIT_SLEEP_MODE 0x11
#define HX8357_ENTER_PARTIAL_MODE 0x12
#define HX8357_ENTER_NORMAL_MODE 0x13
#define HX8357_EXIT_INVERSION_MODE 0x20
#define HX8357_ENTER_INVERSION_MODE 0x21
#define HX8357_SET_DISPLAY_OFF 0x28
#define HX8357_SET_DISPLAY_ON 0x29
#define HX8357_SET_COLUMN_ADDRESS 0x2a
#define HX8357_SET_PAGE_ADDRESS 0x2b
#define HX8357_WRITE_MEMORY_START 0x2c
#define HX8357_READ_MEMORY_START 0x2e
#define HX8357_SET_PARTIAL_AREA 0x30
#define HX8357_SET_SCROLL_AREA 0x33
#define HX8357_SET_TEAR_OFF 0x34
#define HX8357_SET_TEAR_ON 0x35
#define HX8357_SET_ADDRESS_MODE 0x36
#define HX8357_SET_SCROLL_START 0x37
#define HX8357_EXIT_IDLE_MODE 0x38
#define HX8357_ENTER_IDLE_MODE 0x39
#define HX8357_SET_PIXEL_FORMAT 0x3a
#define HX8357_SET_PIXEL_FORMAT_DBI_3BIT (0x1)
#define HX8357_SET_PIXEL_FORMAT_DBI_16BIT (0x5)
#define HX8357_SET_PIXEL_FORMAT_DBI_18BIT (0x6)
#define HX8357_SET_PIXEL_FORMAT_DPI_3BIT (0x1 << 4)
#define HX8357_SET_PIXEL_FORMAT_DPI_16BIT (0x5 << 4)
#define HX8357_SET_PIXEL_FORMAT_DPI_18BIT (0x6 << 4)
#define HX8357_WRITE_MEMORY_CONTINUE 0x3c
#define HX8357_READ_MEMORY_CONTINUE 0x3e
#define HX8357_SET_TEAR_SCAN_LINES 0x44
#define HX8357_GET_SCAN_LINES 0x45
#define HX8357_READ_DDB_START 0xa1
#define HX8357_SET_DISPLAY_MODE 0xb4
#define HX8357_SET_DISPLAY_MODE_RGB_THROUGH (0x3)
#define HX8357_SET_DISPLAY_MODE_RGB_INTERFACE (1 << 4)
#define HX8357_SET_PANEL_DRIVING 0xc0
#define HX8357_SET_DISPLAY_FRAME 0xc5
#define HX8357_SET_RGB 0xc6
#define HX8357_SET_RGB_ENABLE_HIGH (1 << 1)
#define HX8357_SET_GAMMA 0xc8
#define HX8357_SET_POWER 0xd0
#define HX8357_SET_VCOM 0xd1
#define HX8357_SET_POWER_NORMAL 0xd2
#define HX8357_SET_PANEL_RELATED 0xe9
struct hx8357_data {
unsigned im_pins[HX8357_NUM_IM_PINS];
unsigned reset;
struct spi_device *spi;
int state;
};
static u8 hx8357_seq_power[] = {
HX8357_SET_POWER, 0x44, 0x41, 0x06,
};
static u8 hx8357_seq_vcom[] = {
HX8357_SET_VCOM, 0x40, 0x10,
};
static u8 hx8357_seq_power_normal[] = {
HX8357_SET_POWER_NORMAL, 0x05, 0x12,
};
static u8 hx8357_seq_panel_driving[] = {
HX8357_SET_PANEL_DRIVING, 0x14, 0x3b, 0x00, 0x02, 0x11,
};
static u8 hx8357_seq_display_frame[] = {
HX8357_SET_DISPLAY_FRAME, 0x0c,
};
static u8 hx8357_seq_panel_related[] = {
HX8357_SET_PANEL_RELATED, 0x01,
};
static u8 hx8357_seq_undefined1[] = {
0xea, 0x03, 0x00, 0x00,
};
static u8 hx8357_seq_undefined2[] = {
0xeb, 0x40, 0x54, 0x26, 0xdb,
};
static u8 hx8357_seq_gamma[] = {
HX8357_SET_GAMMA, 0x00, 0x15, 0x00, 0x22, 0x00,
0x08, 0x77, 0x26, 0x77, 0x22, 0x04, 0x00,
};
static u8 hx8357_seq_address_mode[] = {
HX8357_SET_ADDRESS_MODE, 0xc0,
};
static u8 hx8357_seq_pixel_format[] = {
HX8357_SET_PIXEL_FORMAT,
HX8357_SET_PIXEL_FORMAT_DPI_18BIT |
HX8357_SET_PIXEL_FORMAT_DBI_18BIT,
};
static u8 hx8357_seq_column_address[] = {
HX8357_SET_COLUMN_ADDRESS, 0x00, 0x00, 0x01, 0x3f,
};
static u8 hx8357_seq_page_address[] = {
HX8357_SET_PAGE_ADDRESS, 0x00, 0x00, 0x01, 0xdf,
};
static u8 hx8357_seq_rgb[] = {
HX8357_SET_RGB, 0x02,
};
static u8 hx8357_seq_display_mode[] = {
HX8357_SET_DISPLAY_MODE,
HX8357_SET_DISPLAY_MODE_RGB_THROUGH |
HX8357_SET_DISPLAY_MODE_RGB_INTERFACE,
};
static int hx8357_spi_write_then_read(struct lcd_device *lcdev,
u8 *txbuf, u16 txlen,
u8 *rxbuf, u16 rxlen)
{
struct hx8357_data *lcd = lcd_get_data(lcdev);
struct spi_message msg;
struct spi_transfer xfer[2];
u16 *local_txbuf = NULL;
int ret = 0;
memset(xfer, 0, sizeof(xfer));
spi_message_init(&msg);
if (txlen) {
int i;
local_txbuf = kcalloc(txlen, sizeof(*local_txbuf), GFP_KERNEL);
if (!local_txbuf)
return -ENOMEM;
for (i = 0; i < txlen; i++) {
local_txbuf[i] = txbuf[i];
if (i > 0)
local_txbuf[i] |= 1 << 8;
}
xfer[0].len = 2 * txlen;
xfer[0].bits_per_word = 9;
xfer[0].tx_buf = local_txbuf;
spi_message_add_tail(&xfer[0], &msg);
}
if (rxlen) {
xfer[1].len = rxlen;
xfer[1].bits_per_word = 8;
xfer[1].rx_buf = rxbuf;
spi_message_add_tail(&xfer[1], &msg);
}
ret = spi_sync(lcd->spi, &msg);
if (ret < 0)
dev_err(&lcdev->dev, "Couldn't send SPI data\n");
if (txlen)
kfree(local_txbuf);
return ret;
}
static inline int hx8357_spi_write_array(struct lcd_device *lcdev,
u8 *value, u8 len)
{
return hx8357_spi_write_then_read(lcdev, value, len, NULL, 0);
}
static inline int hx8357_spi_write_byte(struct lcd_device *lcdev,
u8 value)
{
return hx8357_spi_write_then_read(lcdev, &value, 1, NULL, 0);
}
static int hx8357_enter_standby(struct lcd_device *lcdev)
{
int ret;
ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_OFF);
if (ret < 0)
return ret;
usleep_range(10000, 12000);
ret = hx8357_spi_write_byte(lcdev, HX8357_ENTER_SLEEP_MODE);
if (ret < 0)
return ret;
msleep(120);
return 0;
}
static int hx8357_exit_standby(struct lcd_device *lcdev)
{
int ret;
ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE);
if (ret < 0)
return ret;
msleep(120);
ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON);
if (ret < 0)
return ret;
return 0;
}
static int hx8357_lcd_init(struct lcd_device *lcdev)
{
struct hx8357_data *lcd = lcd_get_data(lcdev);
int ret;
/*
* Set the interface selection pins to SPI mode, with three
* wires
*/
gpio_set_value_cansleep(lcd->im_pins[0], 1);
gpio_set_value_cansleep(lcd->im_pins[1], 0);
gpio_set_value_cansleep(lcd->im_pins[2], 1);
/* Reset the screen */
gpio_set_value(lcd->reset, 1);
usleep_range(10000, 12000);
gpio_set_value(lcd->reset, 0);
usleep_range(10000, 12000);
gpio_set_value(lcd->reset, 1);
msleep(120);
ret = hx8357_spi_write_array(lcdev, hx8357_seq_power,
ARRAY_SIZE(hx8357_seq_power));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_vcom,
ARRAY_SIZE(hx8357_seq_vcom));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_power_normal,
ARRAY_SIZE(hx8357_seq_power_normal));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_driving,
ARRAY_SIZE(hx8357_seq_panel_driving));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_frame,
ARRAY_SIZE(hx8357_seq_display_frame));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_panel_related,
ARRAY_SIZE(hx8357_seq_panel_related));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined1,
ARRAY_SIZE(hx8357_seq_undefined1));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_undefined2,
ARRAY_SIZE(hx8357_seq_undefined2));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_gamma,
ARRAY_SIZE(hx8357_seq_gamma));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_address_mode,
ARRAY_SIZE(hx8357_seq_address_mode));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_pixel_format,
ARRAY_SIZE(hx8357_seq_pixel_format));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_column_address,
ARRAY_SIZE(hx8357_seq_column_address));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_page_address,
ARRAY_SIZE(hx8357_seq_page_address));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_rgb,
ARRAY_SIZE(hx8357_seq_rgb));
if (ret < 0)
return ret;
ret = hx8357_spi_write_array(lcdev, hx8357_seq_display_mode,
ARRAY_SIZE(hx8357_seq_display_mode));
if (ret < 0)
return ret;
ret = hx8357_spi_write_byte(lcdev, HX8357_EXIT_SLEEP_MODE);
if (ret < 0)
return ret;
msleep(120);
ret = hx8357_spi_write_byte(lcdev, HX8357_SET_DISPLAY_ON);
if (ret < 0)
return ret;
usleep_range(5000, 7000);
ret = hx8357_spi_write_byte(lcdev, HX8357_WRITE_MEMORY_START);
if (ret < 0)
return ret;
return 0;
}
#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
static int hx8357_set_power(struct lcd_device *lcdev, int power)
{
struct hx8357_data *lcd = lcd_get_data(lcdev);
int ret = 0;
if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->state))
ret = hx8357_exit_standby(lcdev);
else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->state))
ret = hx8357_enter_standby(lcdev);
if (ret == 0)
lcd->state = power;
else
dev_warn(&lcdev->dev, "failed to set power mode %d\n", power);
return ret;
}
static int hx8357_get_power(struct lcd_device *lcdev)
{
struct hx8357_data *lcd = lcd_get_data(lcdev);
return lcd->state;
}
static struct lcd_ops hx8357_ops = {
.set_power = hx8357_set_power,
.get_power = hx8357_get_power,
};
static int hx8357_probe(struct spi_device *spi)
{
struct lcd_device *lcdev;
struct hx8357_data *lcd;
int i, ret;
lcd = devm_kzalloc(&spi->dev, sizeof(*lcd), GFP_KERNEL);
if (!lcd) {
dev_err(&spi->dev, "Couldn't allocate lcd internal structure!\n");
return -ENOMEM;
}
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "SPI setup failed.\n");
return ret;
}
lcd->spi = spi;
lcd->reset = of_get_named_gpio(spi->dev.of_node, "gpios-reset", 0);
if (!gpio_is_valid(lcd->reset)) {
dev_err(&spi->dev, "Missing dt property: gpios-reset\n");
return -EINVAL;
}
ret = devm_gpio_request_one(&spi->dev, lcd->reset,
GPIOF_OUT_INIT_HIGH,
"hx8357-reset");
if (ret) {
dev_err(&spi->dev,
"failed to request gpio %d: %d\n",
lcd->reset, ret);
return -EINVAL;
}
for (i = 0; i < HX8357_NUM_IM_PINS; i++) {
lcd->im_pins[i] = of_get_named_gpio(spi->dev.of_node,
"im-gpios", i);
if (lcd->im_pins[i] == -EPROBE_DEFER) {
dev_info(&spi->dev, "GPIO requested is not here yet, deferring the probe\n");
return -EPROBE_DEFER;
}
if (!gpio_is_valid(lcd->im_pins[i])) {
dev_err(&spi->dev, "Missing dt property: im-gpios\n");
return -EINVAL;
}
ret = devm_gpio_request_one(&spi->dev, lcd->im_pins[i],
GPIOF_OUT_INIT_LOW, "im_pins");
if (ret) {
dev_err(&spi->dev, "failed to request gpio %d: %d\n",
lcd->im_pins[i], ret);
return -EINVAL;
}
}
lcdev = lcd_device_register("mxsfb", &spi->dev, lcd, &hx8357_ops);
if (IS_ERR(lcdev)) {
ret = PTR_ERR(lcdev);
return ret;
}
spi_set_drvdata(spi, lcdev);
ret = hx8357_lcd_init(lcdev);
if (ret) {
dev_err(&spi->dev, "Couldn't initialize panel\n");
goto init_error;
}
dev_info(&spi->dev, "Panel probed\n");
return 0;
init_error:
lcd_device_unregister(lcdev);
return ret;
}
static int hx8357_remove(struct spi_device *spi)
{
struct lcd_device *lcdev = spi_get_drvdata(spi);
lcd_device_unregister(lcdev);
return 0;
}
static const struct of_device_id hx8357_dt_ids[] = {
{ .compatible = "himax,hx8357" },
{},
};
MODULE_DEVICE_TABLE(of, hx8357_dt_ids);
static struct spi_driver hx8357_driver = {
.probe = hx8357_probe,
.remove = hx8357_remove,
.driver = {
.name = "hx8357",
.of_match_table = of_match_ptr(hx8357_dt_ids),
},
};
module_spi_driver(hx8357_driver);
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
MODULE_DESCRIPTION("Himax HX-8357 LCD Driver");
MODULE_LICENSE("GPL");

View file

@ -49,7 +49,7 @@ static void l4f00242t03_reset(unsigned int gpio)
static void l4f00242t03_lcd_init(struct spi_device *spi) static void l4f00242t03_lcd_init(struct spi_device *spi)
{ {
struct l4f00242t03_pdata *pdata = spi->dev.platform_data; struct l4f00242t03_pdata *pdata = spi->dev.platform_data;
struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) }; const u16 cmd[] = { 0x36, param(0), 0x3A, param(0x60) };
dev_dbg(&spi->dev, "initializing LCD\n"); dev_dbg(&spi->dev, "initializing LCD\n");
@ -70,7 +70,7 @@ static void l4f00242t03_lcd_init(struct spi_device *spi)
static void l4f00242t03_lcd_powerdown(struct spi_device *spi) static void l4f00242t03_lcd_powerdown(struct spi_device *spi)
{ {
struct l4f00242t03_pdata *pdata = spi->dev.platform_data; struct l4f00242t03_pdata *pdata = spi->dev.platform_data;
struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
dev_dbg(&spi->dev, "Powering down LCD\n"); dev_dbg(&spi->dev, "Powering down LCD\n");
@ -168,7 +168,7 @@ static int l4f00242t03_probe(struct spi_device *spi)
return -ENOMEM; return -ENOMEM;
} }
dev_set_drvdata(&spi->dev, priv); spi_set_drvdata(spi, priv);
spi->bits_per_word = 9; spi->bits_per_word = 9;
spi_setup(spi); spi_setup(spi);
@ -190,27 +190,24 @@ static int l4f00242t03_probe(struct spi_device *spi)
return ret; return ret;
} }
priv->io_reg = regulator_get(&spi->dev, "vdd"); priv->io_reg = devm_regulator_get(&spi->dev, "vdd");
if (IS_ERR(priv->io_reg)) { if (IS_ERR(priv->io_reg)) {
dev_err(&spi->dev, "%s: Unable to get the IO regulator\n", dev_err(&spi->dev, "%s: Unable to get the IO regulator\n",
__func__); __func__);
return PTR_ERR(priv->io_reg); return PTR_ERR(priv->io_reg);
} }
priv->core_reg = regulator_get(&spi->dev, "vcore"); priv->core_reg = devm_regulator_get(&spi->dev, "vcore");
if (IS_ERR(priv->core_reg)) { if (IS_ERR(priv->core_reg)) {
ret = PTR_ERR(priv->core_reg);
dev_err(&spi->dev, "%s: Unable to get the core regulator\n", dev_err(&spi->dev, "%s: Unable to get the core regulator\n",
__func__); __func__);
goto err1; return PTR_ERR(priv->core_reg);
} }
priv->ld = lcd_device_register("l4f00242t03", priv->ld = lcd_device_register("l4f00242t03",
&spi->dev, priv, &l4f_ops); &spi->dev, priv, &l4f_ops);
if (IS_ERR(priv->ld)) { if (IS_ERR(priv->ld))
ret = PTR_ERR(priv->ld); return PTR_ERR(priv->ld);
goto err2;
}
/* Init the LCD */ /* Init the LCD */
l4f00242t03_lcd_init(spi); l4f00242t03_lcd_init(spi);
@ -220,33 +217,22 @@ static int l4f00242t03_probe(struct spi_device *spi)
dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n"); dev_info(&spi->dev, "Epson l4f00242t03 lcd probed.\n");
return 0; return 0;
err2:
regulator_put(priv->core_reg);
err1:
regulator_put(priv->io_reg);
return ret;
} }
static int l4f00242t03_remove(struct spi_device *spi) static int l4f00242t03_remove(struct spi_device *spi)
{ {
struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);
lcd_device_unregister(priv->ld); lcd_device_unregister(priv->ld);
spi_set_drvdata(spi, NULL);
dev_set_drvdata(&spi->dev, NULL);
regulator_put(priv->io_reg);
regulator_put(priv->core_reg);
return 0; return 0;
} }
static void l4f00242t03_shutdown(struct spi_device *spi) static void l4f00242t03_shutdown(struct spi_device *spi)
{ {
struct l4f00242t03_priv *priv = dev_get_drvdata(&spi->dev); struct l4f00242t03_priv *priv = spi_get_drvdata(spi);
if (priv) if (priv)
l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN); l4f00242t03_lcd_power_set(priv->ld, FB_BLANK_POWERDOWN);

View file

@ -9,29 +9,20 @@
* under the terms of the GNU General Public License as published by the * 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 * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * 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/wait.h> #include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/fb.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/lcd.h> #include <linux/lcd.h>
#include <linux/backlight.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/wait.h>
#include "ld9040_gamma.h" #include "ld9040_gamma.h"
@ -43,7 +34,6 @@
#define MIN_BRIGHTNESS 0 #define MIN_BRIGHTNESS 0
#define MAX_BRIGHTNESS 24 #define MAX_BRIGHTNESS 24
#define power_is_on(pwr) ((pwr) <= FB_BLANK_NORMAL)
struct ld9040 { struct ld9040 {
struct device *dev; struct device *dev;
@ -78,7 +68,7 @@ static void ld9040_regulator_enable(struct ld9040 *lcd)
lcd->enabled = true; lcd->enabled = true;
} }
mdelay(pd->power_on_delay); msleep(pd->power_on_delay);
out: out:
mutex_unlock(&lcd->lock); mutex_unlock(&lcd->lock);
} }
@ -474,8 +464,9 @@ static int ld9040_panel_send_sequence(struct ld9040 *lcd,
ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]); ret = ld9040_spi_write(lcd, wbuf[i], wbuf[i+1]);
if (ret) if (ret)
break; break;
} else } else {
udelay(wbuf[i+1]*1000); msleep(wbuf[i+1]);
}
i += 2; i += 2;
} }
@ -513,14 +504,9 @@ gamma_err:
static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma) static int ld9040_gamma_ctl(struct ld9040 *lcd, int gamma)
{ {
int ret = 0; return _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
ret = _ld9040_gamma_ctl(lcd, gamma_table.gamma_22_table[gamma]);
return ret;
} }
static int ld9040_ldi_init(struct ld9040 *lcd) static int ld9040_ldi_init(struct ld9040 *lcd)
{ {
int ret, i; int ret, i;
@ -539,7 +525,7 @@ static int ld9040_ldi_init(struct ld9040 *lcd)
for (i = 0; i < ARRAY_SIZE(init_seq); i++) { for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
ret = ld9040_panel_send_sequence(lcd, init_seq[i]); ret = ld9040_panel_send_sequence(lcd, init_seq[i]);
/* workaround: minimum delay time for transferring CMD */ /* workaround: minimum delay time for transferring CMD */
udelay(300); usleep_range(300, 310);
if (ret) if (ret)
break; break;
} }
@ -549,11 +535,7 @@ static int ld9040_ldi_init(struct ld9040 *lcd)
static int ld9040_ldi_enable(struct ld9040 *lcd) static int ld9040_ldi_enable(struct ld9040 *lcd)
{ {
int ret = 0; return ld9040_panel_send_sequence(lcd, seq_display_on);
ret = ld9040_panel_send_sequence(lcd, seq_display_on);
return ret;
} }
static int ld9040_ldi_disable(struct ld9040 *lcd) static int ld9040_ldi_disable(struct ld9040 *lcd)
@ -566,25 +548,27 @@ static int ld9040_ldi_disable(struct ld9040 *lcd)
return ret; return ret;
} }
static int ld9040_power_is_on(int power)
{
return power <= FB_BLANK_NORMAL;
}
static int ld9040_power_on(struct ld9040 *lcd) static int ld9040_power_on(struct ld9040 *lcd)
{ {
int ret = 0; int ret = 0;
struct lcd_platform_data *pd = NULL; struct lcd_platform_data *pd;
pd = lcd->lcd_pd; pd = lcd->lcd_pd;
if (!pd) {
dev_err(lcd->dev, "platform data is NULL.\n");
return -EFAULT;
}
/* lcd power on */ /* lcd power on */
ld9040_regulator_enable(lcd); ld9040_regulator_enable(lcd);
if (!pd->reset) { if (!pd->reset) {
dev_err(lcd->dev, "reset is NULL.\n"); dev_err(lcd->dev, "reset is NULL.\n");
return -EFAULT; return -EINVAL;
} else { } else {
pd->reset(lcd->ld); pd->reset(lcd->ld);
mdelay(pd->reset_delay); msleep(pd->reset_delay);
} }
ret = ld9040_ldi_init(lcd); ret = ld9040_ldi_init(lcd);
@ -604,14 +588,10 @@ static int ld9040_power_on(struct ld9040 *lcd)
static int ld9040_power_off(struct ld9040 *lcd) static int ld9040_power_off(struct ld9040 *lcd)
{ {
int ret = 0; int ret;
struct lcd_platform_data *pd = NULL; struct lcd_platform_data *pd;
pd = lcd->lcd_pd; pd = lcd->lcd_pd;
if (!pd) {
dev_err(lcd->dev, "platform data is NULL.\n");
return -EFAULT;
}
ret = ld9040_ldi_disable(lcd); ret = ld9040_ldi_disable(lcd);
if (ret) { if (ret) {
@ -619,7 +599,7 @@ static int ld9040_power_off(struct ld9040 *lcd)
return -EIO; return -EIO;
} }
mdelay(pd->power_off_delay); msleep(pd->power_off_delay);
/* lcd power off */ /* lcd power off */
ld9040_regulator_disable(lcd); ld9040_regulator_disable(lcd);
@ -631,9 +611,9 @@ static int ld9040_power(struct ld9040 *lcd, int power)
{ {
int ret = 0; int ret = 0;
if (power_is_on(power) && !power_is_on(lcd->power)) if (ld9040_power_is_on(power) && !ld9040_power_is_on(lcd->power))
ret = ld9040_power_on(lcd); ret = ld9040_power_on(lcd);
else if (!power_is_on(power) && power_is_on(lcd->power)) else if (!ld9040_power_is_on(power) && ld9040_power_is_on(lcd->power))
ret = ld9040_power_off(lcd); ret = ld9040_power_off(lcd);
if (!ret) if (!ret)
@ -698,7 +678,6 @@ static const struct backlight_ops ld9040_backlight_ops = {
.update_status = ld9040_set_brightness, .update_status = ld9040_set_brightness,
}; };
static int ld9040_probe(struct spi_device *spi) static int ld9040_probe(struct spi_device *spi)
{ {
int ret = 0; int ret = 0;
@ -726,22 +705,20 @@ static int ld9040_probe(struct spi_device *spi)
lcd->lcd_pd = spi->dev.platform_data; lcd->lcd_pd = spi->dev.platform_data;
if (!lcd->lcd_pd) { if (!lcd->lcd_pd) {
dev_err(&spi->dev, "platform data is NULL.\n"); dev_err(&spi->dev, "platform data is NULL.\n");
return -EFAULT; return -EINVAL;
} }
mutex_init(&lcd->lock); mutex_init(&lcd->lock);
ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
if (ret) { if (ret) {
dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
return ret; return ret;
} }
ld = lcd_device_register("ld9040", &spi->dev, lcd, &ld9040_lcd_ops); ld = lcd_device_register("ld9040", &spi->dev, lcd, &ld9040_lcd_ops);
if (IS_ERR(ld)) { if (IS_ERR(ld))
ret = PTR_ERR(ld); return PTR_ERR(ld);
goto out_free_regulator;
}
lcd->ld = ld; lcd->ld = ld;
@ -772,30 +749,28 @@ static int ld9040_probe(struct spi_device *spi)
lcd->power = FB_BLANK_POWERDOWN; lcd->power = FB_BLANK_POWERDOWN;
ld9040_power(lcd, FB_BLANK_UNBLANK); ld9040_power(lcd, FB_BLANK_UNBLANK);
} else } else {
lcd->power = FB_BLANK_UNBLANK; lcd->power = FB_BLANK_UNBLANK;
}
dev_set_drvdata(&spi->dev, lcd); spi_set_drvdata(spi, lcd);
dev_info(&spi->dev, "ld9040 panel driver has been probed.\n"); dev_info(&spi->dev, "ld9040 panel driver has been probed.\n");
return 0; return 0;
out_unregister_lcd: out_unregister_lcd:
lcd_device_unregister(lcd->ld); lcd_device_unregister(lcd->ld);
out_free_regulator:
regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
return ret; return ret;
} }
static int ld9040_remove(struct spi_device *spi) static int ld9040_remove(struct spi_device *spi)
{ {
struct ld9040 *lcd = dev_get_drvdata(&spi->dev); struct ld9040 *lcd = spi_get_drvdata(spi);
ld9040_power(lcd, FB_BLANK_POWERDOWN); ld9040_power(lcd, FB_BLANK_POWERDOWN);
backlight_device_unregister(lcd->bd); backlight_device_unregister(lcd->bd);
lcd_device_unregister(lcd->ld); lcd_device_unregister(lcd->ld);
regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
return 0; return 0;
} }
@ -803,8 +778,7 @@ static int ld9040_remove(struct spi_device *spi)
#if defined(CONFIG_PM) #if defined(CONFIG_PM)
static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg) static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg)
{ {
int ret = 0; struct ld9040 *lcd = spi_get_drvdata(spi);
struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
@ -812,21 +786,16 @@ static int ld9040_suspend(struct spi_device *spi, pm_message_t mesg)
* when lcd panel is suspend, lcd panel becomes off * when lcd panel is suspend, lcd panel becomes off
* regardless of status. * regardless of status.
*/ */
ret = ld9040_power(lcd, FB_BLANK_POWERDOWN); return ld9040_power(lcd, FB_BLANK_POWERDOWN);
return ret;
} }
static int ld9040_resume(struct spi_device *spi) static int ld9040_resume(struct spi_device *spi)
{ {
int ret = 0; struct ld9040 *lcd = spi_get_drvdata(spi);
struct ld9040 *lcd = dev_get_drvdata(&spi->dev);
lcd->power = FB_BLANK_POWERDOWN; lcd->power = FB_BLANK_POWERDOWN;
ret = ld9040_power(lcd, FB_BLANK_UNBLANK); return ld9040_power(lcd, FB_BLANK_UNBLANK);
return ret;
} }
#else #else
#define ld9040_suspend NULL #define ld9040_suspend NULL
@ -836,7 +805,7 @@ static int ld9040_resume(struct spi_device *spi)
/* Power down all displays on reboot, poweroff or halt. */ /* Power down all displays on reboot, poweroff or halt. */
static void ld9040_shutdown(struct spi_device *spi) static void ld9040_shutdown(struct spi_device *spi)
{ {
struct ld9040 *lcd = dev_get_drvdata(&spi->dev); struct ld9040 *lcd = spi_get_drvdata(spi);
ld9040_power(lcd, FB_BLANK_POWERDOWN); ld9040_power(lcd, FB_BLANK_POWERDOWN);
} }

View file

@ -320,7 +320,7 @@ static int lm3630_backlight_register(struct lm3630_chip_data *pchip,
backlight_device_register(name, pchip->dev, pchip, backlight_device_register(name, pchip->dev, pchip,
&lm3630_bank_a_ops, &props); &lm3630_bank_a_ops, &props);
if (IS_ERR(pchip->bled1)) if (IS_ERR(pchip->bled1))
return -EIO; return PTR_ERR(pchip->bled1);
break; break;
case BLED_2: case BLED_2:
props.brightness = pdata->init_brt_led2; props.brightness = pdata->init_brt_led2;
@ -329,7 +329,7 @@ static int lm3630_backlight_register(struct lm3630_chip_data *pchip,
backlight_device_register(name, pchip->dev, pchip, backlight_device_register(name, pchip->dev, pchip,
&lm3630_bank_b_ops, &props); &lm3630_bank_b_ops, &props);
if (IS_ERR(pchip->bled2)) if (IS_ERR(pchip->bled2))
return -EIO; return PTR_ERR(pchip->bled2);
break; break;
} }
return 0; return 0;

View file

@ -350,14 +350,13 @@ static int lm3639_probe(struct i2c_client *client,
&lm3639_bled_ops, &props); &lm3639_bled_ops, &props);
if (IS_ERR(pchip->bled)) { if (IS_ERR(pchip->bled)) {
dev_err(&client->dev, "fail : backlight register\n"); dev_err(&client->dev, "fail : backlight register\n");
ret = -EIO; ret = PTR_ERR(pchip->bled);
goto err_out; goto err_out;
} }
ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode); ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "failed : add sysfs entries\n"); dev_err(&client->dev, "failed : add sysfs entries\n");
ret = -EIO;
goto err_bled_mode; goto err_bled_mode;
} }
@ -369,7 +368,6 @@ static int lm3639_probe(struct i2c_client *client,
&client->dev, &pchip->cdev_flash); &client->dev, &pchip->cdev_flash);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "fail : flash register\n"); dev_err(&client->dev, "fail : flash register\n");
ret = -EIO;
goto err_flash; goto err_flash;
} }
@ -381,7 +379,6 @@ static int lm3639_probe(struct i2c_client *client,
&client->dev, &pchip->cdev_torch); &client->dev, &pchip->cdev_torch);
if (ret < 0) { if (ret < 0) {
dev_err(&client->dev, "fail : torch register\n"); dev_err(&client->dev, "fail : torch register\n");
ret = -EIO;
goto err_torch; goto err_torch;
} }

View file

@ -180,7 +180,7 @@ static int lms283gf05_probe(struct spi_device *spi)
st->spi = spi; st->spi = spi;
st->ld = ld; st->ld = ld;
dev_set_drvdata(&spi->dev, st); spi_set_drvdata(spi, st);
/* kick in the LCD */ /* kick in the LCD */
if (pdata) if (pdata)
@ -192,7 +192,7 @@ static int lms283gf05_probe(struct spi_device *spi)
static int lms283gf05_remove(struct spi_device *spi) static int lms283gf05_remove(struct spi_device *spi)
{ {
struct lms283gf05_state *st = dev_get_drvdata(&spi->dev); struct lms283gf05_state *st = spi_get_drvdata(spi);
lcd_device_unregister(st->ld); lcd_device_unregister(st->ld);

View file

@ -0,0 +1,441 @@
/*
* lms501kf03 TFT LCD panel driver.
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* Author: Jingoo Han <jg1.han@samsung.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.
*/
#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/lcd.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/wait.h>
#define COMMAND_ONLY 0x00
#define DATA_ONLY 0x01
struct lms501kf03 {
struct device *dev;
struct spi_device *spi;
unsigned int power;
struct lcd_device *ld;
struct lcd_platform_data *lcd_pd;
};
static const unsigned char seq_password[] = {
0xb9, 0xff, 0x83, 0x69,
};
static const unsigned char seq_power[] = {
0xb1, 0x01, 0x00, 0x34, 0x06, 0x00, 0x14, 0x14, 0x20, 0x28,
0x12, 0x12, 0x17, 0x0a, 0x01, 0xe6, 0xe6, 0xe6, 0xe6, 0xe6,
};
static const unsigned char seq_display[] = {
0xb2, 0x00, 0x2b, 0x03, 0x03, 0x70, 0x00, 0xff, 0x00, 0x00,
0x00, 0x00, 0x03, 0x03, 0x00, 0x01,
};
static const unsigned char seq_rgb_if[] = {
0xb3, 0x09,
};
static const unsigned char seq_display_inv[] = {
0xb4, 0x01, 0x08, 0x77, 0x0e, 0x06,
};
static const unsigned char seq_vcom[] = {
0xb6, 0x4c, 0x2e,
};
static const unsigned char seq_gate[] = {
0xd5, 0x00, 0x05, 0x03, 0x29, 0x01, 0x07, 0x17, 0x68, 0x13,
0x37, 0x20, 0x31, 0x8a, 0x46, 0x9b, 0x57, 0x13, 0x02, 0x75,
0xb9, 0x64, 0xa8, 0x07, 0x0f, 0x04, 0x07,
};
static const unsigned char seq_panel[] = {
0xcc, 0x02,
};
static const unsigned char seq_col_mod[] = {
0x3a, 0x77,
};
static const unsigned char seq_w_gamma[] = {
0xe0, 0x00, 0x04, 0x09, 0x0f, 0x1f, 0x3f, 0x1f, 0x2f, 0x0a,
0x0f, 0x10, 0x16, 0x18, 0x16, 0x17, 0x0d, 0x15, 0x00, 0x04,
0x09, 0x0f, 0x38, 0x3f, 0x20, 0x39, 0x0a, 0x0f, 0x10, 0x16,
0x18, 0x16, 0x17, 0x0d, 0x15,
};
static const unsigned char seq_rgb_gamma[] = {
0xc1, 0x01, 0x03, 0x07, 0x0f, 0x1a, 0x22, 0x2c, 0x33, 0x3c,
0x46, 0x4f, 0x58, 0x60, 0x69, 0x71, 0x79, 0x82, 0x89, 0x92,
0x9a, 0xa1, 0xa9, 0xb1, 0xb9, 0xc1, 0xc9, 0xcf, 0xd6, 0xde,
0xe5, 0xec, 0xf3, 0xf9, 0xff, 0xdd, 0x39, 0x07, 0x1c, 0xcb,
0xab, 0x5f, 0x49, 0x80, 0x03, 0x07, 0x0f, 0x19, 0x20, 0x2a,
0x31, 0x39, 0x42, 0x4b, 0x53, 0x5b, 0x63, 0x6b, 0x73, 0x7b,
0x83, 0x8a, 0x92, 0x9b, 0xa2, 0xaa, 0xb2, 0xba, 0xc2, 0xca,
0xd0, 0xd8, 0xe1, 0xe8, 0xf0, 0xf8, 0xff, 0xf7, 0xd8, 0xbe,
0xa7, 0x39, 0x40, 0x85, 0x8c, 0xc0, 0x04, 0x07, 0x0c, 0x17,
0x1c, 0x23, 0x2b, 0x34, 0x3b, 0x43, 0x4c, 0x54, 0x5b, 0x63,
0x6a, 0x73, 0x7a, 0x82, 0x8a, 0x91, 0x98, 0xa1, 0xa8, 0xb0,
0xb7, 0xc1, 0xc9, 0xcf, 0xd9, 0xe3, 0xea, 0xf4, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const unsigned char seq_up_dn[] = {
0x36, 0x10,
};
static const unsigned char seq_sleep_in[] = {
0x10,
};
static const unsigned char seq_sleep_out[] = {
0x11,
};
static const unsigned char seq_display_on[] = {
0x29,
};
static const unsigned char seq_display_off[] = {
0x10,
};
static int lms501kf03_spi_write_byte(struct lms501kf03 *lcd, int addr, int data)
{
u16 buf[1];
struct spi_message msg;
struct spi_transfer xfer = {
.len = 2,
.tx_buf = buf,
};
buf[0] = (addr << 8) | data;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
return spi_sync(lcd->spi, &msg);
}
static int lms501kf03_spi_write(struct lms501kf03 *lcd, unsigned char address,
unsigned char command)
{
return lms501kf03_spi_write_byte(lcd, address, command);
}
static int lms501kf03_panel_send_sequence(struct lms501kf03 *lcd,
const unsigned char *wbuf,
unsigned int len)
{
int ret = 0, i = 0;
while (i < len) {
if (i == 0)
ret = lms501kf03_spi_write(lcd, COMMAND_ONLY, wbuf[i]);
else
ret = lms501kf03_spi_write(lcd, DATA_ONLY, wbuf[i]);
if (ret)
break;
i += 1;
}
return ret;
}
static int lms501kf03_ldi_init(struct lms501kf03 *lcd)
{
int ret, i;
static const unsigned char *init_seq[] = {
seq_password,
seq_power,
seq_display,
seq_rgb_if,
seq_display_inv,
seq_vcom,
seq_gate,
seq_panel,
seq_col_mod,
seq_w_gamma,
seq_rgb_gamma,
seq_sleep_out,
};
static const unsigned int size_seq[] = {
ARRAY_SIZE(seq_password),
ARRAY_SIZE(seq_power),
ARRAY_SIZE(seq_display),
ARRAY_SIZE(seq_rgb_if),
ARRAY_SIZE(seq_display_inv),
ARRAY_SIZE(seq_vcom),
ARRAY_SIZE(seq_gate),
ARRAY_SIZE(seq_panel),
ARRAY_SIZE(seq_col_mod),
ARRAY_SIZE(seq_w_gamma),
ARRAY_SIZE(seq_rgb_gamma),
ARRAY_SIZE(seq_sleep_out),
};
for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
ret = lms501kf03_panel_send_sequence(lcd, init_seq[i],
size_seq[i]);
if (ret)
break;
}
/*
* According to the datasheet, 120ms delay time is required.
* After sleep out sequence, command is blocked for 120ms.
* Thus, LDI should wait for 120ms.
*/
msleep(120);
return ret;
}
static int lms501kf03_ldi_enable(struct lms501kf03 *lcd)
{
return lms501kf03_panel_send_sequence(lcd, seq_display_on,
ARRAY_SIZE(seq_display_on));
}
static int lms501kf03_ldi_disable(struct lms501kf03 *lcd)
{
return lms501kf03_panel_send_sequence(lcd, seq_display_off,
ARRAY_SIZE(seq_display_off));
}
static int lms501kf03_power_is_on(int power)
{
return (power) <= FB_BLANK_NORMAL;
}
static int lms501kf03_power_on(struct lms501kf03 *lcd)
{
int ret = 0;
struct lcd_platform_data *pd;
pd = lcd->lcd_pd;
if (!pd->power_on) {
dev_err(lcd->dev, "power_on is NULL.\n");
return -EINVAL;
} else {
pd->power_on(lcd->ld, 1);
msleep(pd->power_on_delay);
}
if (!pd->reset) {
dev_err(lcd->dev, "reset is NULL.\n");
return -EINVAL;
} else {
pd->reset(lcd->ld);
msleep(pd->reset_delay);
}
ret = lms501kf03_ldi_init(lcd);
if (ret) {
dev_err(lcd->dev, "failed to initialize ldi.\n");
return ret;
}
ret = lms501kf03_ldi_enable(lcd);
if (ret) {
dev_err(lcd->dev, "failed to enable ldi.\n");
return ret;
}
return 0;
}
static int lms501kf03_power_off(struct lms501kf03 *lcd)
{
int ret = 0;
struct lcd_platform_data *pd;
pd = lcd->lcd_pd;
ret = lms501kf03_ldi_disable(lcd);
if (ret) {
dev_err(lcd->dev, "lcd setting failed.\n");
return -EIO;
}
msleep(pd->power_off_delay);
pd->power_on(lcd->ld, 0);
return 0;
}
static int lms501kf03_power(struct lms501kf03 *lcd, int power)
{
int ret = 0;
if (lms501kf03_power_is_on(power) &&
!lms501kf03_power_is_on(lcd->power))
ret = lms501kf03_power_on(lcd);
else if (!lms501kf03_power_is_on(power) &&
lms501kf03_power_is_on(lcd->power))
ret = lms501kf03_power_off(lcd);
if (!ret)
lcd->power = power;
return ret;
}
static int lms501kf03_get_power(struct lcd_device *ld)
{
struct lms501kf03 *lcd = lcd_get_data(ld);
return lcd->power;
}
static int lms501kf03_set_power(struct lcd_device *ld, int power)
{
struct lms501kf03 *lcd = lcd_get_data(ld);
if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
power != FB_BLANK_NORMAL) {
dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
return -EINVAL;
}
return lms501kf03_power(lcd, power);
}
static struct lcd_ops lms501kf03_lcd_ops = {
.get_power = lms501kf03_get_power,
.set_power = lms501kf03_set_power,
};
static int lms501kf03_probe(struct spi_device *spi)
{
struct lms501kf03 *lcd = NULL;
struct lcd_device *ld = NULL;
int ret = 0;
lcd = devm_kzalloc(&spi->dev, sizeof(struct lms501kf03), GFP_KERNEL);
if (!lcd)
return -ENOMEM;
/* lms501kf03 lcd panel uses 3-wire 9-bit SPI Mode. */
spi->bits_per_word = 9;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "spi setup failed.\n");
return ret;
}
lcd->spi = spi;
lcd->dev = &spi->dev;
lcd->lcd_pd = spi->dev.platform_data;
if (!lcd->lcd_pd) {
dev_err(&spi->dev, "platform data is NULL\n");
return -EINVAL;
}
ld = lcd_device_register("lms501kf03", &spi->dev, lcd,
&lms501kf03_lcd_ops);
if (IS_ERR(ld))
return PTR_ERR(ld);
lcd->ld = ld;
if (!lcd->lcd_pd->lcd_enabled) {
/*
* if lcd panel was off from bootloader then
* current lcd status is powerdown and then
* it enables lcd panel.
*/
lcd->power = FB_BLANK_POWERDOWN;
lms501kf03_power(lcd, FB_BLANK_UNBLANK);
} else {
lcd->power = FB_BLANK_UNBLANK;
}
spi_set_drvdata(spi, lcd);
dev_info(&spi->dev, "lms501kf03 panel driver has been probed.\n");
return 0;
}
static int lms501kf03_remove(struct spi_device *spi)
{
struct lms501kf03 *lcd = spi_get_drvdata(spi);
lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
lcd_device_unregister(lcd->ld);
return 0;
}
#if defined(CONFIG_PM)
static int lms501kf03_suspend(struct spi_device *spi, pm_message_t mesg)
{
struct lms501kf03 *lcd = spi_get_drvdata(spi);
dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
/*
* when lcd panel is suspend, lcd panel becomes off
* regardless of status.
*/
return lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
}
static int lms501kf03_resume(struct spi_device *spi)
{
struct lms501kf03 *lcd = spi_get_drvdata(spi);
lcd->power = FB_BLANK_POWERDOWN;
return lms501kf03_power(lcd, FB_BLANK_UNBLANK);
}
#else
#define lms501kf03_suspend NULL
#define lms501kf03_resume NULL
#endif
static void lms501kf03_shutdown(struct spi_device *spi)
{
struct lms501kf03 *lcd = spi_get_drvdata(spi);
lms501kf03_power(lcd, FB_BLANK_POWERDOWN);
}
static struct spi_driver lms501kf03_driver = {
.driver = {
.name = "lms501kf03",
.owner = THIS_MODULE,
},
.probe = lms501kf03_probe,
.remove = lms501kf03_remove,
.shutdown = lms501kf03_shutdown,
.suspend = lms501kf03_suspend,
.resume = lms501kf03_resume,
};
module_spi_driver(lms501kf03_driver);
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("lms501kf03 LCD Driver");
MODULE_LICENSE("GPL");

View file

@ -17,21 +17,48 @@
#include <linux/platform_data/lp855x.h> #include <linux/platform_data/lp855x.h>
#include <linux/pwm.h> #include <linux/pwm.h>
/* Registers */ /* LP8550/1/2/3/6 Registers */
#define BRIGHTNESS_CTRL 0x00 #define LP855X_BRIGHTNESS_CTRL 0x00
#define DEVICE_CTRL 0x01 #define LP855X_DEVICE_CTRL 0x01
#define EEPROM_START 0xA0 #define LP855X_EEPROM_START 0xA0
#define EEPROM_END 0xA7 #define LP855X_EEPROM_END 0xA7
#define EPROM_START 0xA0 #define LP8556_EPROM_START 0xA0
#define EPROM_END 0xAF #define LP8556_EPROM_END 0xAF
/* LP8557 Registers */
#define LP8557_BL_CMD 0x00
#define LP8557_BL_MASK 0x01
#define LP8557_BL_ON 0x01
#define LP8557_BL_OFF 0x00
#define LP8557_BRIGHTNESS_CTRL 0x04
#define LP8557_CONFIG 0x10
#define LP8557_EPROM_START 0x10
#define LP8557_EPROM_END 0x1E
#define BUF_SIZE 20 #define BUF_SIZE 20
#define DEFAULT_BL_NAME "lcd-backlight" #define DEFAULT_BL_NAME "lcd-backlight"
#define MAX_BRIGHTNESS 255 #define MAX_BRIGHTNESS 255
struct lp855x;
/*
* struct lp855x_device_config
* @pre_init_device: init device function call before updating the brightness
* @reg_brightness: register address for brigthenss control
* @reg_devicectrl: register address for device control
* @post_init_device: late init device function call
*/
struct lp855x_device_config {
int (*pre_init_device)(struct lp855x *);
u8 reg_brightness;
u8 reg_devicectrl;
int (*post_init_device)(struct lp855x *);
};
struct lp855x { struct lp855x {
const char *chipname; const char *chipname;
enum lp855x_chip_id chip_id; enum lp855x_chip_id chip_id;
struct lp855x_device_config *cfg;
struct i2c_client *client; struct i2c_client *client;
struct backlight_device *bl; struct backlight_device *bl;
struct device *dev; struct device *dev;
@ -39,9 +66,15 @@ struct lp855x {
struct pwm_device *pwm; struct pwm_device *pwm;
}; };
static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data) static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
{
return i2c_smbus_write_byte_data(lp->client, reg, data);
}
static int lp855x_update_bit(struct lp855x *lp, u8 reg, u8 mask, u8 data)
{ {
int ret; int ret;
u8 tmp;
ret = i2c_smbus_read_byte_data(lp->client, reg); ret = i2c_smbus_read_byte_data(lp->client, reg);
if (ret < 0) { if (ret < 0) {
@ -49,13 +82,11 @@ static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data)
return ret; return ret;
} }
*data = (u8)ret; tmp = (u8)ret;
return 0; tmp &= ~mask;
} tmp |= data & mask;
static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data) return lp855x_write_byte(lp, reg, tmp);
{
return i2c_smbus_write_byte_data(lp->client, reg, data);
} }
static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr) static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
@ -67,12 +98,16 @@ static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
case LP8551: case LP8551:
case LP8552: case LP8552:
case LP8553: case LP8553:
start = EEPROM_START; start = LP855X_EEPROM_START;
end = EEPROM_END; end = LP855X_EEPROM_END;
break; break;
case LP8556: case LP8556:
start = EPROM_START; start = LP8556_EPROM_START;
end = EPROM_END; end = LP8556_EPROM_END;
break;
case LP8557:
start = LP8557_EPROM_START;
end = LP8557_EPROM_END;
break; break;
default: default:
return false; return false;
@ -81,21 +116,76 @@ static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
return (addr >= start && addr <= end); return (addr >= start && addr <= end);
} }
static int lp855x_init_registers(struct lp855x *lp) static int lp8557_bl_off(struct lp855x *lp)
{
/* BL_ON = 0 before updating EPROM settings */
return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
LP8557_BL_OFF);
}
static int lp8557_bl_on(struct lp855x *lp)
{
/* BL_ON = 1 after updating EPROM settings */
return lp855x_update_bit(lp, LP8557_BL_CMD, LP8557_BL_MASK,
LP8557_BL_ON);
}
static struct lp855x_device_config lp855x_dev_cfg = {
.reg_brightness = LP855X_BRIGHTNESS_CTRL,
.reg_devicectrl = LP855X_DEVICE_CTRL,
};
static struct lp855x_device_config lp8557_dev_cfg = {
.reg_brightness = LP8557_BRIGHTNESS_CTRL,
.reg_devicectrl = LP8557_CONFIG,
.pre_init_device = lp8557_bl_off,
.post_init_device = lp8557_bl_on,
};
/*
* Device specific configuration flow
*
* a) pre_init_device(optional)
* b) update the brightness register
* c) update device control register
* d) update ROM area(optional)
* e) post_init_device(optional)
*
*/
static int lp855x_configure(struct lp855x *lp)
{ {
u8 val, addr; u8 val, addr;
int i, ret; int i, ret;
struct lp855x_platform_data *pd = lp->pdata; struct lp855x_platform_data *pd = lp->pdata;
switch (lp->chip_id) {
case LP8550 ... LP8556:
lp->cfg = &lp855x_dev_cfg;
break;
case LP8557:
lp->cfg = &lp8557_dev_cfg;
break;
default:
return -EINVAL;
}
if (lp->cfg->pre_init_device) {
ret = lp->cfg->pre_init_device(lp);
if (ret) {
dev_err(lp->dev, "pre init device err: %d\n", ret);
goto err;
}
}
val = pd->initial_brightness; val = pd->initial_brightness;
ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); ret = lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
if (ret) if (ret)
return ret; goto err;
val = pd->device_control; val = pd->device_control;
ret = lp855x_write_byte(lp, DEVICE_CTRL, val); ret = lp855x_write_byte(lp, lp->cfg->reg_devicectrl, val);
if (ret) if (ret)
return ret; goto err;
if (pd->load_new_rom_data && pd->size_program) { if (pd->load_new_rom_data && pd->size_program) {
for (i = 0; i < pd->size_program; i++) { for (i = 0; i < pd->size_program; i++) {
@ -106,10 +196,21 @@ static int lp855x_init_registers(struct lp855x *lp)
ret = lp855x_write_byte(lp, addr, val); ret = lp855x_write_byte(lp, addr, val);
if (ret) if (ret)
return ret; goto err;
} }
} }
if (lp->cfg->post_init_device) {
ret = lp->cfg->post_init_device(lp);
if (ret) {
dev_err(lp->dev, "post init device err: %d\n", ret);
goto err;
}
}
return 0;
err:
return ret; return ret;
} }
@ -151,7 +252,7 @@ static int lp855x_bl_update_status(struct backlight_device *bl)
} else if (mode == REGISTER_BASED) { } else if (mode == REGISTER_BASED) {
u8 val = bl->props.brightness; u8 val = bl->props.brightness;
lp855x_write_byte(lp, BRIGHTNESS_CTRL, val); lp855x_write_byte(lp, lp->cfg->reg_brightness, val);
} }
return 0; return 0;
@ -159,16 +260,6 @@ static int lp855x_bl_update_status(struct backlight_device *bl)
static int lp855x_bl_get_brightness(struct backlight_device *bl) static int lp855x_bl_get_brightness(struct backlight_device *bl)
{ {
struct lp855x *lp = bl_get_data(bl);
enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
if (mode == REGISTER_BASED) {
u8 val = 0;
lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val);
bl->props.brightness = val;
}
return bl->props.brightness; return bl->props.brightness;
} }
@ -271,11 +362,10 @@ static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
lp->chip_id = id->driver_data; lp->chip_id = id->driver_data;
i2c_set_clientdata(cl, lp); i2c_set_clientdata(cl, lp);
ret = lp855x_init_registers(lp); ret = lp855x_configure(lp);
if (ret) { if (ret) {
dev_err(lp->dev, "i2c communication err: %d", ret); dev_err(lp->dev, "device config err: %d", ret);
if (mode == REGISTER_BASED) goto err_dev;
goto err_dev;
} }
ret = lp855x_backlight_register(lp); ret = lp855x_backlight_register(lp);
@ -318,6 +408,7 @@ static const struct i2c_device_id lp855x_ids[] = {
{"lp8552", LP8552}, {"lp8552", LP8552},
{"lp8553", LP8553}, {"lp8553", LP8553},
{"lp8556", LP8556}, {"lp8556", LP8556},
{"lp8557", LP8557},
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, lp855x_ids); MODULE_DEVICE_TABLE(i2c, lp855x_ids);

View file

@ -252,7 +252,7 @@ static int ltv350qv_probe(struct spi_device *spi)
if (ret) if (ret)
goto out_unregister; goto out_unregister;
dev_set_drvdata(&spi->dev, lcd); spi_set_drvdata(spi, lcd);
return 0; return 0;
@ -263,7 +263,7 @@ out_unregister:
static int ltv350qv_remove(struct spi_device *spi) static int ltv350qv_remove(struct spi_device *spi)
{ {
struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); struct ltv350qv *lcd = spi_get_drvdata(spi);
ltv350qv_power(lcd, FB_BLANK_POWERDOWN); ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
lcd_device_unregister(lcd->ld); lcd_device_unregister(lcd->ld);
@ -274,14 +274,14 @@ static int ltv350qv_remove(struct spi_device *spi)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state) static int ltv350qv_suspend(struct spi_device *spi, pm_message_t state)
{ {
struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); struct ltv350qv *lcd = spi_get_drvdata(spi);
return ltv350qv_power(lcd, FB_BLANK_POWERDOWN); return ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
} }
static int ltv350qv_resume(struct spi_device *spi) static int ltv350qv_resume(struct spi_device *spi)
{ {
struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); struct ltv350qv *lcd = spi_get_drvdata(spi);
return ltv350qv_power(lcd, FB_BLANK_UNBLANK); return ltv350qv_power(lcd, FB_BLANK_UNBLANK);
} }
@ -293,7 +293,7 @@ static int ltv350qv_resume(struct spi_device *spi)
/* Power down all displays on reboot, poweroff or halt */ /* Power down all displays on reboot, poweroff or halt */
static void ltv350qv_shutdown(struct spi_device *spi) static void ltv350qv_shutdown(struct spi_device *spi)
{ {
struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); struct ltv350qv *lcd = spi_get_drvdata(spi);
ltv350qv_power(lcd, FB_BLANK_POWERDOWN); ltv350qv_power(lcd, FB_BLANK_POWERDOWN);
} }

View file

@ -77,7 +77,7 @@ static void omapbl_blank(struct omap_backlight *bl, int mode)
static int omapbl_suspend(struct platform_device *pdev, pm_message_t state) static int omapbl_suspend(struct platform_device *pdev, pm_message_t state)
{ {
struct backlight_device *dev = platform_get_drvdata(pdev); struct backlight_device *dev = platform_get_drvdata(pdev);
struct omap_backlight *bl = dev_get_drvdata(&dev->dev); struct omap_backlight *bl = bl_get_data(dev);
omapbl_blank(bl, FB_BLANK_POWERDOWN); omapbl_blank(bl, FB_BLANK_POWERDOWN);
return 0; return 0;
@ -86,7 +86,7 @@ static int omapbl_suspend(struct platform_device *pdev, pm_message_t state)
static int omapbl_resume(struct platform_device *pdev) static int omapbl_resume(struct platform_device *pdev)
{ {
struct backlight_device *dev = platform_get_drvdata(pdev); struct backlight_device *dev = platform_get_drvdata(pdev);
struct omap_backlight *bl = dev_get_drvdata(&dev->dev); struct omap_backlight *bl = bl_get_data(dev);
omapbl_blank(bl, bl->powermode); omapbl_blank(bl, bl->powermode);
return 0; return 0;
@ -98,7 +98,7 @@ static int omapbl_resume(struct platform_device *pdev)
static int omapbl_set_power(struct backlight_device *dev, int state) static int omapbl_set_power(struct backlight_device *dev, int state)
{ {
struct omap_backlight *bl = dev_get_drvdata(&dev->dev); struct omap_backlight *bl = bl_get_data(dev);
omapbl_blank(bl, state); omapbl_blank(bl, state);
bl->powermode = state; bl->powermode = state;
@ -108,7 +108,7 @@ static int omapbl_set_power(struct backlight_device *dev, int state)
static int omapbl_update_status(struct backlight_device *dev) static int omapbl_update_status(struct backlight_device *dev)
{ {
struct omap_backlight *bl = dev_get_drvdata(&dev->dev); struct omap_backlight *bl = bl_get_data(dev);
if (bl->current_intensity != dev->props.brightness) { if (bl->current_intensity != dev->props.brightness) {
if (bl->powermode == FB_BLANK_UNBLANK) if (bl->powermode == FB_BLANK_UNBLANK)
@ -124,7 +124,7 @@ static int omapbl_update_status(struct backlight_device *dev)
static int omapbl_get_intensity(struct backlight_device *dev) static int omapbl_get_intensity(struct backlight_device *dev)
{ {
struct omap_backlight *bl = dev_get_drvdata(&dev->dev); struct omap_backlight *bl = bl_get_data(dev);
return bl->current_intensity; return bl->current_intensity;
} }

View file

@ -37,7 +37,7 @@ struct pwm_bl_data {
static int pwm_backlight_update_status(struct backlight_device *bl) static int pwm_backlight_update_status(struct backlight_device *bl)
{ {
struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); struct pwm_bl_data *pb = bl_get_data(bl);
int brightness = bl->props.brightness; int brightness = bl->props.brightness;
int max = bl->props.max_brightness; int max = bl->props.max_brightness;
@ -83,7 +83,7 @@ static int pwm_backlight_get_brightness(struct backlight_device *bl)
static int pwm_backlight_check_fb(struct backlight_device *bl, static int pwm_backlight_check_fb(struct backlight_device *bl,
struct fb_info *info) struct fb_info *info)
{ {
struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); struct pwm_bl_data *pb = bl_get_data(bl);
return !pb->check_fb || pb->check_fb(pb->dev, info); return !pb->check_fb || pb->check_fb(pb->dev, info);
} }
@ -264,7 +264,7 @@ err_alloc:
static int pwm_backlight_remove(struct platform_device *pdev) static int pwm_backlight_remove(struct platform_device *pdev)
{ {
struct backlight_device *bl = platform_get_drvdata(pdev); struct backlight_device *bl = platform_get_drvdata(pdev);
struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); struct pwm_bl_data *pb = bl_get_data(bl);
backlight_device_unregister(bl); backlight_device_unregister(bl);
pwm_config(pb->pwm, 0, pb->period); pwm_config(pb->pwm, 0, pb->period);
@ -278,7 +278,7 @@ static int pwm_backlight_remove(struct platform_device *pdev)
static int pwm_backlight_suspend(struct device *dev) static int pwm_backlight_suspend(struct device *dev)
{ {
struct backlight_device *bl = dev_get_drvdata(dev); struct backlight_device *bl = dev_get_drvdata(dev);
struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev); struct pwm_bl_data *pb = bl_get_data(bl);
if (pb->notify) if (pb->notify)
pb->notify(pb->dev, 0); pb->notify(pb->dev, 0);

View file

@ -9,28 +9,19 @@
* under the terms of the GNU General Public License as published by the * 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 * Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. * 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/wait.h> #include <linux/backlight.h>
#include <linux/fb.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/fb.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/irq.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/lcd.h> #include <linux/lcd.h>
#include <linux/backlight.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/wait.h>
#include "s6e63m0_gamma.h" #include "s6e63m0_gamma.h"
@ -43,8 +34,6 @@
#define MIN_BRIGHTNESS 0 #define MIN_BRIGHTNESS 0
#define MAX_BRIGHTNESS 10 #define MAX_BRIGHTNESS 10
#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL)
struct s6e63m0 { struct s6e63m0 {
struct device *dev; struct device *dev;
struct spi_device *spi; struct spi_device *spi;
@ -57,7 +46,7 @@ struct s6e63m0 {
struct lcd_platform_data *lcd_pd; struct lcd_platform_data *lcd_pd;
}; };
static const unsigned short SEQ_PANEL_CONDITION_SET[] = { static const unsigned short seq_panel_condition_set[] = {
0xF8, 0x01, 0xF8, 0x01,
DATA_ONLY, 0x27, DATA_ONLY, 0x27,
DATA_ONLY, 0x27, DATA_ONLY, 0x27,
@ -76,7 +65,7 @@ static const unsigned short SEQ_PANEL_CONDITION_SET[] = {
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = { static const unsigned short seq_display_condition_set[] = {
0xf2, 0x02, 0xf2, 0x02,
DATA_ONLY, 0x03, DATA_ONLY, 0x03,
DATA_ONLY, 0x1c, DATA_ONLY, 0x1c,
@ -90,7 +79,7 @@ static const unsigned short SEQ_DISPLAY_CONDITION_SET[] = {
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_GAMMA_SETTING[] = { static const unsigned short seq_gamma_setting[] = {
0xfa, 0x00, 0xfa, 0x00,
DATA_ONLY, 0x18, DATA_ONLY, 0x18,
DATA_ONLY, 0x08, DATA_ONLY, 0x08,
@ -119,7 +108,7 @@ static const unsigned short SEQ_GAMMA_SETTING[] = {
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_ETC_CONDITION_SET[] = { static const unsigned short seq_etc_condition_set[] = {
0xf6, 0x00, 0xf6, 0x00,
DATA_ONLY, 0x8c, DATA_ONLY, 0x8c,
DATA_ONLY, 0x07, DATA_ONLY, 0x07,
@ -318,47 +307,47 @@ static const unsigned short SEQ_ETC_CONDITION_SET[] = {
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_ACL_ON[] = { static const unsigned short seq_acl_on[] = {
/* ACL on */ /* ACL on */
0xc0, 0x01, 0xc0, 0x01,
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_ACL_OFF[] = { static const unsigned short seq_acl_off[] = {
/* ACL off */ /* ACL off */
0xc0, 0x00, 0xc0, 0x00,
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_ELVSS_ON[] = { static const unsigned short seq_elvss_on[] = {
/* ELVSS on */ /* ELVSS on */
0xb1, 0x0b, 0xb1, 0x0b,
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_ELVSS_OFF[] = { static const unsigned short seq_elvss_off[] = {
/* ELVSS off */ /* ELVSS off */
0xb1, 0x0a, 0xb1, 0x0a,
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_STAND_BY_OFF[] = { static const unsigned short seq_stand_by_off[] = {
0x11, COMMAND_ONLY, 0x11, COMMAND_ONLY,
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_STAND_BY_ON[] = { static const unsigned short seq_stand_by_on[] = {
0x10, COMMAND_ONLY, 0x10, COMMAND_ONLY,
ENDDEF, 0x0000 ENDDEF, 0x0000
}; };
static const unsigned short SEQ_DISPLAY_ON[] = { static const unsigned short seq_display_on[] = {
0x29, COMMAND_ONLY, 0x29, COMMAND_ONLY,
ENDDEF, 0x0000 ENDDEF, 0x0000
@ -406,8 +395,9 @@ static int s6e63m0_panel_send_sequence(struct s6e63m0 *lcd,
ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]); ret = s6e63m0_spi_write(lcd, wbuf[i], wbuf[i+1]);
if (ret) if (ret)
break; break;
} else } else {
udelay(wbuf[i+1]*1000); msleep(wbuf[i+1]);
}
i += 2; i += 2;
} }
@ -457,12 +447,12 @@ static int s6e63m0_ldi_init(struct s6e63m0 *lcd)
{ {
int ret, i; int ret, i;
const unsigned short *init_seq[] = { const unsigned short *init_seq[] = {
SEQ_PANEL_CONDITION_SET, seq_panel_condition_set,
SEQ_DISPLAY_CONDITION_SET, seq_display_condition_set,
SEQ_GAMMA_SETTING, seq_gamma_setting,
SEQ_ETC_CONDITION_SET, seq_etc_condition_set,
SEQ_ACL_ON, seq_acl_on,
SEQ_ELVSS_ON, seq_elvss_on,
}; };
for (i = 0; i < ARRAY_SIZE(init_seq); i++) { for (i = 0; i < ARRAY_SIZE(init_seq); i++) {
@ -478,8 +468,8 @@ static int s6e63m0_ldi_enable(struct s6e63m0 *lcd)
{ {
int ret = 0, i; int ret = 0, i;
const unsigned short *enable_seq[] = { const unsigned short *enable_seq[] = {
SEQ_STAND_BY_OFF, seq_stand_by_off,
SEQ_DISPLAY_ON, seq_display_on,
}; };
for (i = 0; i < ARRAY_SIZE(enable_seq); i++) { for (i = 0; i < ARRAY_SIZE(enable_seq); i++) {
@ -495,43 +485,39 @@ static int s6e63m0_ldi_disable(struct s6e63m0 *lcd)
{ {
int ret; int ret;
ret = s6e63m0_panel_send_sequence(lcd, SEQ_STAND_BY_ON); ret = s6e63m0_panel_send_sequence(lcd, seq_stand_by_on);
return ret; return ret;
} }
static int s6e63m0_power_is_on(int power)
{
return power <= FB_BLANK_NORMAL;
}
static int s6e63m0_power_on(struct s6e63m0 *lcd) static int s6e63m0_power_on(struct s6e63m0 *lcd)
{ {
int ret = 0; int ret = 0;
struct lcd_platform_data *pd = NULL; struct lcd_platform_data *pd;
struct backlight_device *bd = NULL; struct backlight_device *bd;
pd = lcd->lcd_pd; pd = lcd->lcd_pd;
if (!pd) {
dev_err(lcd->dev, "platform data is NULL.\n");
return -EFAULT;
}
bd = lcd->bd; bd = lcd->bd;
if (!bd) {
dev_err(lcd->dev, "backlight device is NULL.\n");
return -EFAULT;
}
if (!pd->power_on) { if (!pd->power_on) {
dev_err(lcd->dev, "power_on is NULL.\n"); dev_err(lcd->dev, "power_on is NULL.\n");
return -EFAULT; return -EINVAL;
} else { } else {
pd->power_on(lcd->ld, 1); pd->power_on(lcd->ld, 1);
mdelay(pd->power_on_delay); msleep(pd->power_on_delay);
} }
if (!pd->reset) { if (!pd->reset) {
dev_err(lcd->dev, "reset is NULL.\n"); dev_err(lcd->dev, "reset is NULL.\n");
return -EFAULT; return -EINVAL;
} else { } else {
pd->reset(lcd->ld); pd->reset(lcd->ld);
mdelay(pd->reset_delay); msleep(pd->reset_delay);
} }
ret = s6e63m0_ldi_init(lcd); ret = s6e63m0_ldi_init(lcd);
@ -558,14 +544,10 @@ static int s6e63m0_power_on(struct s6e63m0 *lcd)
static int s6e63m0_power_off(struct s6e63m0 *lcd) static int s6e63m0_power_off(struct s6e63m0 *lcd)
{ {
int ret = 0; int ret;
struct lcd_platform_data *pd = NULL; struct lcd_platform_data *pd;
pd = lcd->lcd_pd; pd = lcd->lcd_pd;
if (!pd) {
dev_err(lcd->dev, "platform data is NULL.\n");
return -EFAULT;
}
ret = s6e63m0_ldi_disable(lcd); ret = s6e63m0_ldi_disable(lcd);
if (ret) { if (ret) {
@ -573,13 +555,9 @@ static int s6e63m0_power_off(struct s6e63m0 *lcd)
return -EIO; return -EIO;
} }
mdelay(pd->power_off_delay); msleep(pd->power_off_delay);
if (!pd->power_on) { pd->power_on(lcd->ld, 0);
dev_err(lcd->dev, "power_on is NULL.\n");
return -EFAULT;
} else
pd->power_on(lcd->ld, 0);
return 0; return 0;
} }
@ -588,9 +566,9 @@ static int s6e63m0_power(struct s6e63m0 *lcd, int power)
{ {
int ret = 0; int ret = 0;
if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) if (s6e63m0_power_is_on(power) && !s6e63m0_power_is_on(lcd->power))
ret = s6e63m0_power_on(lcd); ret = s6e63m0_power_on(lcd);
else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) else if (!s6e63m0_power_is_on(power) && s6e63m0_power_is_on(lcd->power))
ret = s6e63m0_power_off(lcd); ret = s6e63m0_power_off(lcd);
if (!ret) if (!ret)
@ -760,7 +738,7 @@ static int s6e63m0_probe(struct spi_device *spi)
lcd->lcd_pd = spi->dev.platform_data; lcd->lcd_pd = spi->dev.platform_data;
if (!lcd->lcd_pd) { if (!lcd->lcd_pd) {
dev_err(&spi->dev, "platform data is NULL.\n"); dev_err(&spi->dev, "platform data is NULL.\n");
return -EFAULT; return -EINVAL;
} }
ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops); ld = lcd_device_register("s6e63m0", &spi->dev, lcd, &s6e63m0_lcd_ops);
@ -788,7 +766,7 @@ static int s6e63m0_probe(struct spi_device *spi)
* know that. * know that.
*/ */
lcd->gamma_table_count = lcd->gamma_table_count =
sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int)); sizeof(gamma_table) / (MAX_GAMMA_LEVEL * sizeof(int *));
ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode); ret = device_create_file(&(spi->dev), &dev_attr_gamma_mode);
if (ret < 0) if (ret < 0)
@ -811,10 +789,11 @@ static int s6e63m0_probe(struct spi_device *spi)
lcd->power = FB_BLANK_POWERDOWN; lcd->power = FB_BLANK_POWERDOWN;
s6e63m0_power(lcd, FB_BLANK_UNBLANK); s6e63m0_power(lcd, FB_BLANK_UNBLANK);
} else } else {
lcd->power = FB_BLANK_UNBLANK; lcd->power = FB_BLANK_UNBLANK;
}
dev_set_drvdata(&spi->dev, lcd); spi_set_drvdata(spi, lcd);
dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n"); dev_info(&spi->dev, "s6e63m0 panel driver has been probed.\n");
@ -827,7 +806,7 @@ out_lcd_unregister:
static int s6e63m0_remove(struct spi_device *spi) static int s6e63m0_remove(struct spi_device *spi)
{ {
struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); struct s6e63m0 *lcd = spi_get_drvdata(spi);
s6e63m0_power(lcd, FB_BLANK_POWERDOWN); s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
device_remove_file(&spi->dev, &dev_attr_gamma_table); device_remove_file(&spi->dev, &dev_attr_gamma_table);
@ -839,44 +818,26 @@ static int s6e63m0_remove(struct spi_device *spi)
} }
#if defined(CONFIG_PM) #if defined(CONFIG_PM)
static unsigned int before_power;
static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg) static int s6e63m0_suspend(struct spi_device *spi, pm_message_t mesg)
{ {
int ret = 0; struct s6e63m0 *lcd = spi_get_drvdata(spi);
struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power); dev_dbg(&spi->dev, "lcd->power = %d\n", lcd->power);
before_power = lcd->power;
/* /*
* when lcd panel is suspend, lcd panel becomes off * when lcd panel is suspend, lcd panel becomes off
* regardless of status. * regardless of status.
*/ */
ret = s6e63m0_power(lcd, FB_BLANK_POWERDOWN); return s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
return ret;
} }
static int s6e63m0_resume(struct spi_device *spi) static int s6e63m0_resume(struct spi_device *spi)
{ {
int ret = 0; struct s6e63m0 *lcd = spi_get_drvdata(spi);
struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev);
/* lcd->power = FB_BLANK_POWERDOWN;
* after suspended, if lcd panel status is FB_BLANK_UNBLANK
* (at that time, before_power is FB_BLANK_UNBLANK) then
* it changes that status to FB_BLANK_POWERDOWN to get lcd on.
*/
if (before_power == FB_BLANK_UNBLANK)
lcd->power = FB_BLANK_POWERDOWN;
dev_dbg(&spi->dev, "before_power = %d\n", before_power); return s6e63m0_power(lcd, FB_BLANK_UNBLANK);
ret = s6e63m0_power(lcd, before_power);
return ret;
} }
#else #else
#define s6e63m0_suspend NULL #define s6e63m0_suspend NULL
@ -886,7 +847,7 @@ static int s6e63m0_resume(struct spi_device *spi)
/* Power down all displays on reboot, poweroff or halt. */ /* Power down all displays on reboot, poweroff or halt. */
static void s6e63m0_shutdown(struct spi_device *spi) static void s6e63m0_shutdown(struct spi_device *spi)
{ {
struct s6e63m0 *lcd = dev_get_drvdata(&spi->dev); struct s6e63m0 *lcd = spi_get_drvdata(spi);
s6e63m0_power(lcd, FB_BLANK_POWERDOWN); s6e63m0_power(lcd, FB_BLANK_POWERDOWN);
} }

View file

@ -390,7 +390,7 @@ static int tdo24m_probe(struct spi_device *spi)
if (IS_ERR(lcd->lcd_dev)) if (IS_ERR(lcd->lcd_dev))
return PTR_ERR(lcd->lcd_dev); return PTR_ERR(lcd->lcd_dev);
dev_set_drvdata(&spi->dev, lcd); spi_set_drvdata(spi, lcd);
err = tdo24m_power(lcd, FB_BLANK_UNBLANK); err = tdo24m_power(lcd, FB_BLANK_UNBLANK);
if (err) if (err)
goto out_unregister; goto out_unregister;
@ -404,7 +404,7 @@ out_unregister:
static int tdo24m_remove(struct spi_device *spi) static int tdo24m_remove(struct spi_device *spi)
{ {
struct tdo24m *lcd = dev_get_drvdata(&spi->dev); struct tdo24m *lcd = spi_get_drvdata(spi);
tdo24m_power(lcd, FB_BLANK_POWERDOWN); tdo24m_power(lcd, FB_BLANK_POWERDOWN);
lcd_device_unregister(lcd->lcd_dev); lcd_device_unregister(lcd->lcd_dev);
@ -415,14 +415,14 @@ static int tdo24m_remove(struct spi_device *spi)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int tdo24m_suspend(struct spi_device *spi, pm_message_t state) static int tdo24m_suspend(struct spi_device *spi, pm_message_t state)
{ {
struct tdo24m *lcd = dev_get_drvdata(&spi->dev); struct tdo24m *lcd = spi_get_drvdata(spi);
return tdo24m_power(lcd, FB_BLANK_POWERDOWN); return tdo24m_power(lcd, FB_BLANK_POWERDOWN);
} }
static int tdo24m_resume(struct spi_device *spi) static int tdo24m_resume(struct spi_device *spi)
{ {
struct tdo24m *lcd = dev_get_drvdata(&spi->dev); struct tdo24m *lcd = spi_get_drvdata(spi);
return tdo24m_power(lcd, FB_BLANK_UNBLANK); return tdo24m_power(lcd, FB_BLANK_UNBLANK);
} }
@ -434,7 +434,7 @@ static int tdo24m_resume(struct spi_device *spi)
/* Power down all displays on reboot, poweroff or halt */ /* Power down all displays on reboot, poweroff or halt */
static void tdo24m_shutdown(struct spi_device *spi) static void tdo24m_shutdown(struct spi_device *spi)
{ {
struct tdo24m *lcd = dev_get_drvdata(&spi->dev); struct tdo24m *lcd = spi_get_drvdata(spi);
tdo24m_power(lcd, FB_BLANK_POWERDOWN); tdo24m_power(lcd, FB_BLANK_POWERDOWN);
} }

View file

@ -54,7 +54,7 @@ static void tosa_bl_set_backlight(struct tosa_bl_data *data, int brightness)
static int tosa_bl_update_status(struct backlight_device *dev) static int tosa_bl_update_status(struct backlight_device *dev)
{ {
struct backlight_properties *props = &dev->props; struct backlight_properties *props = &dev->props;
struct tosa_bl_data *data = dev_get_drvdata(&dev->dev); struct tosa_bl_data *data = bl_get_data(dev);
int power = max(props->power, props->fb_blank); int power = max(props->power, props->fb_blank);
int brightness = props->brightness; int brightness = props->brightness;

View file

@ -193,7 +193,7 @@ static int tosa_lcd_probe(struct spi_device *spi)
return ret; return ret;
data->spi = spi; data->spi = spi;
dev_set_drvdata(&spi->dev, data); spi_set_drvdata(spi, data);
ret = devm_gpio_request_one(&spi->dev, TOSA_GPIO_TG_ON, ret = devm_gpio_request_one(&spi->dev, TOSA_GPIO_TG_ON,
GPIOF_OUT_INIT_LOW, "tg #pwr"); GPIOF_OUT_INIT_LOW, "tg #pwr");
@ -220,13 +220,13 @@ static int tosa_lcd_probe(struct spi_device *spi)
err_register: err_register:
tosa_lcd_tg_off(data); tosa_lcd_tg_off(data);
err_gpio_tg: err_gpio_tg:
dev_set_drvdata(&spi->dev, NULL); spi_set_drvdata(spi, NULL);
return ret; return ret;
} }
static int tosa_lcd_remove(struct spi_device *spi) static int tosa_lcd_remove(struct spi_device *spi)
{ {
struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); struct tosa_lcd_data *data = spi_get_drvdata(spi);
lcd_device_unregister(data->lcd); lcd_device_unregister(data->lcd);
@ -235,7 +235,7 @@ static int tosa_lcd_remove(struct spi_device *spi)
tosa_lcd_tg_off(data); tosa_lcd_tg_off(data);
dev_set_drvdata(&spi->dev, NULL); spi_set_drvdata(spi, NULL);
return 0; return 0;
} }
@ -243,7 +243,7 @@ static int tosa_lcd_remove(struct spi_device *spi)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state) static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state)
{ {
struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); struct tosa_lcd_data *data = spi_get_drvdata(spi);
tosa_lcd_tg_off(data); tosa_lcd_tg_off(data);
@ -252,7 +252,7 @@ static int tosa_lcd_suspend(struct spi_device *spi, pm_message_t state)
static int tosa_lcd_resume(struct spi_device *spi) static int tosa_lcd_resume(struct spi_device *spi)
{ {
struct tosa_lcd_data *data = dev_get_drvdata(&spi->dev); struct tosa_lcd_data *data = spi_get_drvdata(spi);
tosa_lcd_tg_init(data); tosa_lcd_tg_init(data);
if (POWER_IS_ON(data->lcd_power)) if (POWER_IS_ON(data->lcd_power))

View file

@ -208,12 +208,11 @@ static int vgg2432a4_lcd_init(struct ili9320 *lcd,
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state) static int vgg2432a4_suspend(struct spi_device *spi, pm_message_t state)
{ {
return ili9320_suspend(dev_get_drvdata(&spi->dev), state); return ili9320_suspend(spi_get_drvdata(spi), state);
} }
static int vgg2432a4_resume(struct spi_device *spi) static int vgg2432a4_resume(struct spi_device *spi)
{ {
return ili9320_resume(dev_get_drvdata(&spi->dev)); return ili9320_resume(spi_get_drvdata(spi));
} }
#else #else
#define vgg2432a4_suspend NULL #define vgg2432a4_suspend NULL
@ -242,12 +241,12 @@ static int vgg2432a4_probe(struct spi_device *spi)
static int vgg2432a4_remove(struct spi_device *spi) static int vgg2432a4_remove(struct spi_device *spi)
{ {
return ili9320_remove(dev_get_drvdata(&spi->dev)); return ili9320_remove(spi_get_drvdata(spi));
} }
static void vgg2432a4_shutdown(struct spi_device *spi) static void vgg2432a4_shutdown(struct spi_device *spi)
{ {
ili9320_shutdown(dev_get_drvdata(&spi->dev)); ili9320_shutdown(spi_get_drvdata(spi));
} }
static struct spi_driver vgg2432a4_driver = { static struct spi_driver vgg2432a4_driver = {

View file

@ -1242,8 +1242,16 @@ static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height,
if (!height || !width) if (!height || !width)
return; return;
if (sy < vc->vc_top && vc->vc_top == logo_lines) if (sy < vc->vc_top && vc->vc_top == logo_lines) {
vc->vc_top = 0; vc->vc_top = 0;
/*
* If the font dimensions are not an integral of the display
* dimensions then the ops->clear below won't end up clearing
* the margins. Call clear_margins here in case the logo
* bitmap stretched into the margin area.
*/
fbcon_clear_margins(vc, 0);
}
/* Split blits that cross physical y_wrap boundary */ /* Split blits that cross physical y_wrap boundary */

View file

@ -965,10 +965,11 @@ static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp) static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
{ {
struct device_node *dp_phy_node; struct device_node *dp_phy_node = of_node_get(dp->dev->of_node);
u32 phy_base; u32 phy_base;
int ret = 0;
dp_phy_node = of_find_node_by_name(dp->dev->of_node, "dptx-phy"); dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
if (!dp_phy_node) { if (!dp_phy_node) {
dev_err(dp->dev, "could not find dptx-phy node\n"); dev_err(dp->dev, "could not find dptx-phy node\n");
return -ENODEV; return -ENODEV;
@ -976,22 +977,28 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) { if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
dev_err(dp->dev, "faild to get reg for dptx-phy\n"); dev_err(dp->dev, "faild to get reg for dptx-phy\n");
return -EINVAL; ret = -EINVAL;
goto err;
} }
if (of_property_read_u32(dp_phy_node, "samsung,enable-mask", if (of_property_read_u32(dp_phy_node, "samsung,enable-mask",
&dp->enable_mask)) { &dp->enable_mask)) {
dev_err(dp->dev, "faild to get enable-mask for dptx-phy\n"); dev_err(dp->dev, "faild to get enable-mask for dptx-phy\n");
return -EINVAL; ret = -EINVAL;
goto err;
} }
dp->phy_addr = ioremap(phy_base, SZ_4); dp->phy_addr = ioremap(phy_base, SZ_4);
if (!dp->phy_addr) { if (!dp->phy_addr) {
dev_err(dp->dev, "failed to ioremap dp-phy\n"); dev_err(dp->dev, "failed to ioremap dp-phy\n");
return -ENOMEM; ret = -ENOMEM;
goto err;
} }
return 0; err:
of_node_put(dp_phy_node);
return ret;
} }
static void exynos_dp_phy_init(struct exynos_dp_device *dp) static void exynos_dp_phy_init(struct exynos_dp_device *dp)
@ -1117,8 +1124,6 @@ static int exynos_dp_remove(struct platform_device *pdev)
struct exynos_dp_platdata *pdata = pdev->dev.platform_data; struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
struct exynos_dp_device *dp = platform_get_drvdata(pdev); struct exynos_dp_device *dp = platform_get_drvdata(pdev);
disable_irq(dp->irq);
flush_work(&dp->hotplug_work); flush_work(&dp->hotplug_work);
if (pdev->dev.of_node) { if (pdev->dev.of_node) {
@ -1141,6 +1146,8 @@ static int exynos_dp_suspend(struct device *dev)
struct exynos_dp_platdata *pdata = dev->platform_data; struct exynos_dp_platdata *pdata = dev->platform_data;
struct exynos_dp_device *dp = dev_get_drvdata(dev); struct exynos_dp_device *dp = dev_get_drvdata(dev);
disable_irq(dp->irq);
flush_work(&dp->hotplug_work); flush_work(&dp->hotplug_work);
if (dev->of_node) { if (dev->of_node) {

View file

@ -338,7 +338,8 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
struct mipi_dsim_ddi *dsim_ddi; struct mipi_dsim_ddi *dsim_ddi;
int ret = -EINVAL; int ret = -EINVAL;
dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL); dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device),
GFP_KERNEL);
if (!dsim) { if (!dsim) {
dev_err(&pdev->dev, "failed to allocate dsim object.\n"); dev_err(&pdev->dev, "failed to allocate dsim object.\n");
return -ENOMEM; return -ENOMEM;
@ -352,13 +353,13 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd; dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
if (dsim_pd == NULL) { if (dsim_pd == NULL) {
dev_err(&pdev->dev, "failed to get platform data for dsim.\n"); dev_err(&pdev->dev, "failed to get platform data for dsim.\n");
goto err_clock_get; return -EINVAL;
} }
/* get mipi_dsim_config. */ /* get mipi_dsim_config. */
dsim_config = dsim_pd->dsim_config; dsim_config = dsim_pd->dsim_config;
if (dsim_config == NULL) { if (dsim_config == NULL) {
dev_err(&pdev->dev, "failed to get dsim config data.\n"); dev_err(&pdev->dev, "failed to get dsim config data.\n");
goto err_clock_get; return -EINVAL;
} }
dsim->dsim_config = dsim_config; dsim->dsim_config = dsim_config;
@ -366,41 +367,28 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
mutex_init(&dsim->lock); mutex_init(&dsim->lock);
ret = regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies), supplies); ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies),
supplies);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret); dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
goto err_clock_get; return ret;
} }
dsim->clock = clk_get(&pdev->dev, "dsim0"); dsim->clock = devm_clk_get(&pdev->dev, "dsim0");
if (IS_ERR(dsim->clock)) { if (IS_ERR(dsim->clock)) {
dev_err(&pdev->dev, "failed to get dsim clock source\n"); dev_err(&pdev->dev, "failed to get dsim clock source\n");
ret = -ENODEV; return -ENODEV;
goto err_clock_get;
} }
clk_enable(dsim->clock); clk_enable(dsim->clock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get io memory region\n");
ret = -ENODEV;
goto err_platform_get;
}
dsim->res = request_mem_region(res->start, resource_size(res), dsim->reg_base = devm_request_and_ioremap(&pdev->dev, res);
dev_name(&pdev->dev));
if (!dsim->res) {
dev_err(&pdev->dev, "failed to request io memory region\n");
ret = -ENOMEM;
goto err_mem_region;
}
dsim->reg_base = ioremap(res->start, resource_size(res));
if (!dsim->reg_base) { if (!dsim->reg_base) {
dev_err(&pdev->dev, "failed to remap io region\n"); dev_err(&pdev->dev, "failed to remap io region\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_ioremap; goto error;
} }
mutex_init(&dsim->lock); mutex_init(&dsim->lock);
@ -410,26 +398,27 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
if (!dsim_ddi) { if (!dsim_ddi) {
dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n"); dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n");
ret = -EINVAL; ret = -EINVAL;
goto err_bind; goto error;
} }
dsim->irq = platform_get_irq(pdev, 0); dsim->irq = platform_get_irq(pdev, 0);
if (dsim->irq < 0) { if (IS_ERR_VALUE(dsim->irq)) {
dev_err(&pdev->dev, "failed to request dsim irq resource\n"); dev_err(&pdev->dev, "failed to request dsim irq resource\n");
ret = -EINVAL; ret = -EINVAL;
goto err_platform_get_irq; goto error;
} }
init_completion(&dsim_wr_comp); init_completion(&dsim_wr_comp);
init_completion(&dsim_rd_comp); init_completion(&dsim_rd_comp);
platform_set_drvdata(pdev, dsim); platform_set_drvdata(pdev, dsim);
ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler, ret = devm_request_irq(&pdev->dev, dsim->irq,
exynos_mipi_dsi_interrupt_handler,
IRQF_SHARED, dev_name(&pdev->dev), dsim); IRQF_SHARED, dev_name(&pdev->dev), dsim);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "failed to request dsim irq\n"); dev_err(&pdev->dev, "failed to request dsim irq\n");
ret = -EINVAL; ret = -EINVAL;
goto err_bind; goto error;
} }
/* enable interrupts */ /* enable interrupts */
@ -471,22 +460,8 @@ done:
return 0; return 0;
err_bind: error:
iounmap(dsim->reg_base);
err_ioremap:
release_mem_region(dsim->res->start, resource_size(dsim->res));
err_mem_region:
release_resource(dsim->res);
err_platform_get:
clk_disable(dsim->clock); clk_disable(dsim->clock);
clk_put(dsim->clock);
err_clock_get:
kfree(dsim);
err_platform_get_irq:
return ret; return ret;
} }
@ -496,13 +471,7 @@ static int exynos_mipi_dsi_remove(struct platform_device *pdev)
struct mipi_dsim_ddi *dsim_ddi, *next; struct mipi_dsim_ddi *dsim_ddi, *next;
struct mipi_dsim_lcd_driver *dsim_lcd_drv; struct mipi_dsim_lcd_driver *dsim_lcd_drv;
iounmap(dsim->reg_base);
clk_disable(dsim->clock); clk_disable(dsim->clock);
clk_put(dsim->clock);
release_resource(dsim->res);
release_mem_region(dsim->res->start, resource_size(dsim->res));
list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) { list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
if (dsim_ddi) { if (dsim_ddi) {
@ -518,9 +487,6 @@ static int exynos_mipi_dsi_remove(struct platform_device *pdev)
} }
} }
regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
kfree(dsim);
return 0; return 0;
} }

View file

@ -776,7 +776,7 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
int ret; int ret;
u8 mtp_id[3] = {0, }; u8 mtp_id[3] = {0, };
lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL); lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL);
if (!lcd) { if (!lcd) {
dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n"); dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
return -ENOMEM; return -ENOMEM;
@ -788,18 +788,17 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
mutex_init(&lcd->lock); mutex_init(&lcd->lock);
ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies); ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
if (ret) { if (ret) {
dev_err(lcd->dev, "Failed to get regulators: %d\n", ret); dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
goto err_lcd_register; return ret;
} }
lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd, lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd,
&s6e8ax0_lcd_ops); &s6e8ax0_lcd_ops);
if (IS_ERR(lcd->ld)) { if (IS_ERR(lcd->ld)) {
dev_err(lcd->dev, "failed to register lcd ops.\n"); dev_err(lcd->dev, "failed to register lcd ops.\n");
ret = PTR_ERR(lcd->ld); return PTR_ERR(lcd->ld);
goto err_lcd_register;
} }
lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd, lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd,
@ -838,11 +837,6 @@ static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
err_backlight_register: err_backlight_register:
lcd_device_unregister(lcd->ld); lcd_device_unregister(lcd->ld);
err_lcd_register:
regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
kfree(lcd);
return ret; return ret;
} }

318
drivers/video/goldfishfb.c Normal file
View file

@ -0,0 +1,318 @@
/*
* Copyright (C) 2007 Google, Inc.
* Copyright (C) 2012 Intel, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mm.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
enum {
FB_GET_WIDTH = 0x00,
FB_GET_HEIGHT = 0x04,
FB_INT_STATUS = 0x08,
FB_INT_ENABLE = 0x0c,
FB_SET_BASE = 0x10,
FB_SET_ROTATION = 0x14,
FB_SET_BLANK = 0x18,
FB_GET_PHYS_WIDTH = 0x1c,
FB_GET_PHYS_HEIGHT = 0x20,
FB_INT_VSYNC = 1U << 0,
FB_INT_BASE_UPDATE_DONE = 1U << 1
};
struct goldfish_fb {
void __iomem *reg_base;
int irq;
spinlock_t lock;
wait_queue_head_t wait;
int base_update_count;
int rotation;
struct fb_info fb;
u32 cmap[16];
};
static irqreturn_t goldfish_fb_interrupt(int irq, void *dev_id)
{
unsigned long irq_flags;
struct goldfish_fb *fb = dev_id;
u32 status;
spin_lock_irqsave(&fb->lock, irq_flags);
status = readl(fb->reg_base + FB_INT_STATUS);
if (status & FB_INT_BASE_UPDATE_DONE) {
fb->base_update_count++;
wake_up(&fb->wait);
}
spin_unlock_irqrestore(&fb->lock, irq_flags);
return status ? IRQ_HANDLED : IRQ_NONE;
}
static inline u32 convert_bitfield(int val, struct fb_bitfield *bf)
{
unsigned int mask = (1 << bf->length) - 1;
return (val >> (16 - bf->length) & mask) << bf->offset;
}
static int
goldfish_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green,
unsigned int blue, unsigned int transp, struct fb_info *info)
{
struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
if (regno < 16) {
fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) |
convert_bitfield(blue, &fb->fb.var.blue) |
convert_bitfield(green, &fb->fb.var.green) |
convert_bitfield(red, &fb->fb.var.red);
return 0;
} else {
return 1;
}
}
static int goldfish_fb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
if ((var->rotate & 1) != (info->var.rotate & 1)) {
if ((var->xres != info->var.yres) ||
(var->yres != info->var.xres) ||
(var->xres_virtual != info->var.yres) ||
(var->yres_virtual > info->var.xres * 2) ||
(var->yres_virtual < info->var.xres)) {
return -EINVAL;
}
} else {
if ((var->xres != info->var.xres) ||
(var->yres != info->var.yres) ||
(var->xres_virtual != info->var.xres) ||
(var->yres_virtual > info->var.yres * 2) ||
(var->yres_virtual < info->var.yres)) {
return -EINVAL;
}
}
if ((var->xoffset != info->var.xoffset) ||
(var->bits_per_pixel != info->var.bits_per_pixel) ||
(var->grayscale != info->var.grayscale)) {
return -EINVAL;
}
return 0;
}
static int goldfish_fb_set_par(struct fb_info *info)
{
struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
if (fb->rotation != fb->fb.var.rotate) {
info->fix.line_length = info->var.xres * 2;
fb->rotation = fb->fb.var.rotate;
writel(fb->rotation, fb->reg_base + FB_SET_ROTATION);
}
return 0;
}
static int goldfish_fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
unsigned long irq_flags;
int base_update_count;
struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
spin_lock_irqsave(&fb->lock, irq_flags);
base_update_count = fb->base_update_count;
writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset,
fb->reg_base + FB_SET_BASE);
spin_unlock_irqrestore(&fb->lock, irq_flags);
wait_event_timeout(fb->wait,
fb->base_update_count != base_update_count, HZ / 15);
if (fb->base_update_count == base_update_count)
pr_err("goldfish_fb_pan_display: timeout wating for base update\n");
return 0;
}
static int goldfish_fb_blank(int blank, struct fb_info *info)
{
struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb);
switch (blank) {
case FB_BLANK_NORMAL:
writel(1, fb->reg_base + FB_SET_BLANK);
break;
case FB_BLANK_UNBLANK:
writel(0, fb->reg_base + FB_SET_BLANK);
break;
}
return 0;
}
static struct fb_ops goldfish_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = goldfish_fb_check_var,
.fb_set_par = goldfish_fb_set_par,
.fb_setcolreg = goldfish_fb_setcolreg,
.fb_pan_display = goldfish_fb_pan_display,
.fb_blank = goldfish_fb_blank,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static int goldfish_fb_probe(struct platform_device *pdev)
{
int ret;
struct resource *r;
struct goldfish_fb *fb;
size_t framesize;
u32 width, height;
dma_addr_t fbpaddr;
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
if (fb == NULL) {
ret = -ENOMEM;
goto err_fb_alloc_failed;
}
spin_lock_init(&fb->lock);
init_waitqueue_head(&fb->wait);
platform_set_drvdata(pdev, fb);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL) {
ret = -ENODEV;
goto err_no_io_base;
}
fb->reg_base = ioremap(r->start, PAGE_SIZE);
if (fb->reg_base == NULL) {
ret = -ENOMEM;
goto err_no_io_base;
}
fb->irq = platform_get_irq(pdev, 0);
if (fb->irq <= 0) {
ret = -ENODEV;
goto err_no_irq;
}
width = readl(fb->reg_base + FB_GET_WIDTH);
height = readl(fb->reg_base + FB_GET_HEIGHT);
fb->fb.fbops = &goldfish_fb_ops;
fb->fb.flags = FBINFO_FLAG_DEFAULT;
fb->fb.pseudo_palette = fb->cmap;
fb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
fb->fb.fix.visual = FB_VISUAL_TRUECOLOR;
fb->fb.fix.line_length = width * 2;
fb->fb.fix.accel = FB_ACCEL_NONE;
fb->fb.fix.ypanstep = 1;
fb->fb.var.xres = width;
fb->fb.var.yres = height;
fb->fb.var.xres_virtual = width;
fb->fb.var.yres_virtual = height * 2;
fb->fb.var.bits_per_pixel = 16;
fb->fb.var.activate = FB_ACTIVATE_NOW;
fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT);
fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH);
fb->fb.var.pixclock = 10000;
fb->fb.var.red.offset = 11;
fb->fb.var.red.length = 5;
fb->fb.var.green.offset = 5;
fb->fb.var.green.length = 6;
fb->fb.var.blue.offset = 0;
fb->fb.var.blue.length = 5;
framesize = width * height * 2 * 2;
fb->fb.screen_base = (char __force __iomem *)dma_alloc_coherent(
&pdev->dev, framesize,
&fbpaddr, GFP_KERNEL);
pr_debug("allocating frame buffer %d * %d, got %p\n",
width, height, fb->fb.screen_base);
if (fb->fb.screen_base == NULL) {
ret = -ENOMEM;
goto err_alloc_screen_base_failed;
}
fb->fb.fix.smem_start = fbpaddr;
fb->fb.fix.smem_len = framesize;
ret = fb_set_var(&fb->fb, &fb->fb.var);
if (ret)
goto err_fb_set_var_failed;
ret = request_irq(fb->irq, goldfish_fb_interrupt, IRQF_SHARED,
pdev->name, fb);
if (ret)
goto err_request_irq_failed;
writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE);
goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */
ret = register_framebuffer(&fb->fb);
if (ret)
goto err_register_framebuffer_failed;
return 0;
err_register_framebuffer_failed:
free_irq(fb->irq, fb);
err_request_irq_failed:
err_fb_set_var_failed:
dma_free_coherent(&pdev->dev, framesize,
(void *)fb->fb.screen_base,
fb->fb.fix.smem_start);
err_alloc_screen_base_failed:
err_no_irq:
iounmap(fb->reg_base);
err_no_io_base:
kfree(fb);
err_fb_alloc_failed:
return ret;
}
static int goldfish_fb_remove(struct platform_device *pdev)
{
size_t framesize;
struct goldfish_fb *fb = platform_get_drvdata(pdev);
framesize = fb->fb.var.xres_virtual * fb->fb.var.yres_virtual * 2;
unregister_framebuffer(&fb->fb);
free_irq(fb->irq, fb);
dma_free_coherent(&pdev->dev, framesize, (void *)fb->fb.screen_base,
fb->fb.fix.smem_start);
iounmap(fb->reg_base);
return 0;
}
static struct platform_driver goldfish_fb_driver = {
.probe = goldfish_fb_probe,
.remove = goldfish_fb_remove,
.driver = {
.name = "goldfish_fb"
}
};
module_platform_driver(goldfish_fb_driver);
MODULE_LICENSE("GPL v2");

11
drivers/video/mmp/Kconfig Normal file
View file

@ -0,0 +1,11 @@
menuconfig MMP_DISP
tristate "Marvell MMP Display Subsystem support"
depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988
help
Marvell Display Subsystem support.
if MMP_DISP
source "drivers/video/mmp/hw/Kconfig"
source "drivers/video/mmp/panel/Kconfig"
source "drivers/video/mmp/fb/Kconfig"
endif

View file

@ -0,0 +1 @@
obj-y += core.o hw/ panel/ fb/

258
drivers/video/mmp/core.c Normal file
View file

@ -0,0 +1,258 @@
/*
* linux/drivers/video/mmp/common.c
* This driver is a common framework for Marvell Display Controller
*
* Copyright (C) 2012 Marvell Technology Group Ltd.
* Authors: Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <video/mmp_disp.h>
static struct mmp_overlay *path_get_overlay(struct mmp_path *path,
int overlay_id)
{
if (path && overlay_id < path->overlay_num)
return &path->overlays[overlay_id];
return 0;
}
static int path_check_status(struct mmp_path *path)
{
int i;
for (i = 0; i < path->overlay_num; i++)
if (path->overlays[i].status)
return 1;
return 0;
}
/*
* Get modelist write pointer of modelist.
* It also returns modelist number
* this function fetches modelist from phy/panel:
* for HDMI/parallel or dsi to hdmi cases, get from phy
* or get from panel
*/
static int path_get_modelist(struct mmp_path *path,
struct mmp_mode **modelist)
{
BUG_ON(!path || !modelist);
if (path->panel && path->panel->get_modelist)
return path->panel->get_modelist(path->panel, modelist);
return 0;
}
/*
* panel list is used to pair panel/path when path/panel registered
* path list is used for both buffer driver and platdriver
* plat driver do path register/unregister
* panel driver do panel register/unregister
* buffer driver get registered path
*/
static LIST_HEAD(panel_list);
static LIST_HEAD(path_list);
static DEFINE_MUTEX(disp_lock);
/*
* mmp_register_panel - register panel to panel_list and connect to path
* @p: panel to be registered
*
* this function provides interface for panel drivers to register panel
* to panel_list and connect to path which matchs panel->plat_path_name.
* no error returns when no matching path is found as path register after
* panel register is permitted.
*/
void mmp_register_panel(struct mmp_panel *panel)
{
struct mmp_path *path;
mutex_lock(&disp_lock);
/* add */
list_add_tail(&panel->node, &panel_list);
/* try to register to path */
list_for_each_entry(path, &path_list, node) {
if (!strcmp(panel->plat_path_name, path->name)) {
dev_info(panel->dev, "connect to path %s\n",
path->name);
path->panel = panel;
break;
}
}
mutex_unlock(&disp_lock);
}
EXPORT_SYMBOL_GPL(mmp_register_panel);
/*
* mmp_unregister_panel - unregister panel from panel_list and disconnect
* @p: panel to be unregistered
*
* this function provides interface for panel drivers to unregister panel
* from panel_list and disconnect from path.
*/
void mmp_unregister_panel(struct mmp_panel *panel)
{
struct mmp_path *path;
mutex_lock(&disp_lock);
list_del(&panel->node);
list_for_each_entry(path, &path_list, node) {
if (path->panel && path->panel == panel) {
dev_info(panel->dev, "disconnect from path %s\n",
path->name);
path->panel = NULL;
break;
}
}
mutex_unlock(&disp_lock);
}
EXPORT_SYMBOL_GPL(mmp_unregister_panel);
/*
* mmp_get_path - get path by name
* @p: path name
*
* this function checks path name in path_list and return matching path
* return NULL if no matching path
*/
struct mmp_path *mmp_get_path(const char *name)
{
struct mmp_path *path;
int found = 0;
mutex_lock(&disp_lock);
list_for_each_entry(path, &path_list, node) {
if (!strcmp(name, path->name)) {
found = 1;
break;
}
}
mutex_unlock(&disp_lock);
return found ? path : NULL;
}
EXPORT_SYMBOL_GPL(mmp_get_path);
/*
* mmp_register_path - init and register path by path_info
* @p: path info provided by display controller
*
* this function init by path info and register path to path_list
* this function also try to connect path with panel by name
*/
struct mmp_path *mmp_register_path(struct mmp_path_info *info)
{
int i;
size_t size;
struct mmp_path *path = NULL;
struct mmp_panel *panel;
size = sizeof(struct mmp_path)
+ sizeof(struct mmp_overlay) * info->overlay_num;
path = kzalloc(size, GFP_KERNEL);
if (!path)
goto failed;
/* path set */
mutex_init(&path->access_ok);
path->dev = info->dev;
path->id = info->id;
path->name = info->name;
path->output_type = info->output_type;
path->overlay_num = info->overlay_num;
path->plat_data = info->plat_data;
path->ops.set_mode = info->set_mode;
mutex_lock(&disp_lock);
/* get panel */
list_for_each_entry(panel, &panel_list, node) {
if (!strcmp(info->name, panel->plat_path_name)) {
dev_info(path->dev, "get panel %s\n", panel->name);
path->panel = panel;
break;
}
}
dev_info(path->dev, "register %s, overlay_num %d\n",
path->name, path->overlay_num);
/* default op set: if already set by driver, never cover it */
if (!path->ops.check_status)
path->ops.check_status = path_check_status;
if (!path->ops.get_overlay)
path->ops.get_overlay = path_get_overlay;
if (!path->ops.get_modelist)
path->ops.get_modelist = path_get_modelist;
/* step3: init overlays */
for (i = 0; i < path->overlay_num; i++) {
path->overlays[i].path = path;
path->overlays[i].id = i;
mutex_init(&path->overlays[i].access_ok);
path->overlays[i].ops = info->overlay_ops;
}
/* add to pathlist */
list_add_tail(&path->node, &path_list);
mutex_unlock(&disp_lock);
return path;
failed:
kfree(path);
mutex_unlock(&disp_lock);
return NULL;
}
EXPORT_SYMBOL_GPL(mmp_register_path);
/*
* mmp_unregister_path - unregister and destory path
* @p: path to be destoried.
*
* this function registers path and destorys it.
*/
void mmp_unregister_path(struct mmp_path *path)
{
int i;
if (!path)
return;
mutex_lock(&disp_lock);
/* del from pathlist */
list_del(&path->node);
/* deinit overlays */
for (i = 0; i < path->overlay_num; i++)
mutex_destroy(&path->overlays[i].access_ok);
mutex_destroy(&path->access_ok);
kfree(path);
mutex_unlock(&disp_lock);
dev_info(path->dev, "de-register %s\n", path->name);
}
EXPORT_SYMBOL_GPL(mmp_unregister_path);

View file

@ -0,0 +1,13 @@
if MMP_DISP
config MMP_FB
bool "fb driver for Marvell MMP Display Subsystem"
depends on FB
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
default y
help
fb driver for Marvell MMP Display Subsystem
endif

View file

@ -0,0 +1 @@
obj-$(CONFIG_MMP_FB) += mmpfb.o

View file

@ -0,0 +1,685 @@
/*
* linux/drivers/video/mmp/fb/mmpfb.c
* Framebuffer driver for Marvell Display controller.
*
* Copyright (C) 2012 Marvell Technology Group Ltd.
* Authors: Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include "mmpfb.h"
static int var_to_pixfmt(struct fb_var_screeninfo *var)
{
/*
* Pseudocolor mode?
*/
if (var->bits_per_pixel == 8)
return PIXFMT_PSEUDOCOLOR;
/*
* Check for YUV422PLANAR.
*/
if (var->bits_per_pixel == 16 && var->red.length == 8 &&
var->green.length == 4 && var->blue.length == 4) {
if (var->green.offset >= var->blue.offset)
return PIXFMT_YUV422P;
else
return PIXFMT_YVU422P;
}
/*
* Check for YUV420PLANAR.
*/
if (var->bits_per_pixel == 12 && var->red.length == 8 &&
var->green.length == 2 && var->blue.length == 2) {
if (var->green.offset >= var->blue.offset)
return PIXFMT_YUV420P;
else
return PIXFMT_YVU420P;
}
/*
* Check for YUV422PACK.
*/
if (var->bits_per_pixel == 16 && var->red.length == 16 &&
var->green.length == 16 && var->blue.length == 16) {
if (var->red.offset == 0)
return PIXFMT_YUYV;
else if (var->green.offset >= var->blue.offset)
return PIXFMT_UYVY;
else
return PIXFMT_VYUY;
}
/*
* Check for 565/1555.
*/
if (var->bits_per_pixel == 16 && var->red.length <= 5 &&
var->green.length <= 6 && var->blue.length <= 5) {
if (var->transp.length == 0) {
if (var->red.offset >= var->blue.offset)
return PIXFMT_RGB565;
else
return PIXFMT_BGR565;
}
}
/*
* Check for 888/A888.
*/
if (var->bits_per_pixel <= 32 && var->red.length <= 8 &&
var->green.length <= 8 && var->blue.length <= 8) {
if (var->bits_per_pixel == 24 && var->transp.length == 0) {
if (var->red.offset >= var->blue.offset)
return PIXFMT_RGB888PACK;
else
return PIXFMT_BGR888PACK;
}
if (var->bits_per_pixel == 32 && var->transp.offset == 24) {
if (var->red.offset >= var->blue.offset)
return PIXFMT_RGBA888;
else
return PIXFMT_BGRA888;
} else {
if (var->red.offset >= var->blue.offset)
return PIXFMT_RGB888UNPACK;
else
return PIXFMT_BGR888UNPACK;
}
/* fall through */
}
return -EINVAL;
}
static void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt)
{
switch (pix_fmt) {
case PIXFMT_RGB565:
var->bits_per_pixel = 16;
var->red.offset = 11; var->red.length = 5;
var->green.offset = 5; var->green.length = 6;
var->blue.offset = 0; var->blue.length = 5;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_BGR565:
var->bits_per_pixel = 16;
var->red.offset = 0; var->red.length = 5;
var->green.offset = 5; var->green.length = 6;
var->blue.offset = 11; var->blue.length = 5;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_RGB888UNPACK:
var->bits_per_pixel = 32;
var->red.offset = 16; var->red.length = 8;
var->green.offset = 8; var->green.length = 8;
var->blue.offset = 0; var->blue.length = 8;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_BGR888UNPACK:
var->bits_per_pixel = 32;
var->red.offset = 0; var->red.length = 8;
var->green.offset = 8; var->green.length = 8;
var->blue.offset = 16; var->blue.length = 8;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_RGBA888:
var->bits_per_pixel = 32;
var->red.offset = 16; var->red.length = 8;
var->green.offset = 8; var->green.length = 8;
var->blue.offset = 0; var->blue.length = 8;
var->transp.offset = 24; var->transp.length = 8;
break;
case PIXFMT_BGRA888:
var->bits_per_pixel = 32;
var->red.offset = 0; var->red.length = 8;
var->green.offset = 8; var->green.length = 8;
var->blue.offset = 16; var->blue.length = 8;
var->transp.offset = 24; var->transp.length = 8;
break;
case PIXFMT_RGB888PACK:
var->bits_per_pixel = 24;
var->red.offset = 16; var->red.length = 8;
var->green.offset = 8; var->green.length = 8;
var->blue.offset = 0; var->blue.length = 8;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_BGR888PACK:
var->bits_per_pixel = 24;
var->red.offset = 0; var->red.length = 8;
var->green.offset = 8; var->green.length = 8;
var->blue.offset = 16; var->blue.length = 8;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_YUV420P:
var->bits_per_pixel = 12;
var->red.offset = 4; var->red.length = 8;
var->green.offset = 2; var->green.length = 2;
var->blue.offset = 0; var->blue.length = 2;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_YVU420P:
var->bits_per_pixel = 12;
var->red.offset = 4; var->red.length = 8;
var->green.offset = 0; var->green.length = 2;
var->blue.offset = 2; var->blue.length = 2;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_YUV422P:
var->bits_per_pixel = 16;
var->red.offset = 8; var->red.length = 8;
var->green.offset = 4; var->green.length = 4;
var->blue.offset = 0; var->blue.length = 4;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_YVU422P:
var->bits_per_pixel = 16;
var->red.offset = 8; var->red.length = 8;
var->green.offset = 0; var->green.length = 4;
var->blue.offset = 4; var->blue.length = 4;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_UYVY:
var->bits_per_pixel = 16;
var->red.offset = 8; var->red.length = 16;
var->green.offset = 4; var->green.length = 16;
var->blue.offset = 0; var->blue.length = 16;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_VYUY:
var->bits_per_pixel = 16;
var->red.offset = 8; var->red.length = 16;
var->green.offset = 0; var->green.length = 16;
var->blue.offset = 4; var->blue.length = 16;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_YUYV:
var->bits_per_pixel = 16;
var->red.offset = 0; var->red.length = 16;
var->green.offset = 4; var->green.length = 16;
var->blue.offset = 8; var->blue.length = 16;
var->transp.offset = 0; var->transp.length = 0;
break;
case PIXFMT_PSEUDOCOLOR:
var->bits_per_pixel = 8;
var->red.offset = 0; var->red.length = 8;
var->green.offset = 0; var->green.length = 8;
var->blue.offset = 0; var->blue.length = 8;
var->transp.offset = 0; var->transp.length = 0;
break;
}
}
/*
* fb framework has its limitation:
* 1. input color/output color is not seprated
* 2. fb_videomode not include output color
* so for fb usage, we keep a output format which is not changed
* then it's added for mmpmode
*/
static void fbmode_to_mmpmode(struct mmp_mode *mode,
struct fb_videomode *videomode, int output_fmt)
{
u64 div_result = 1000000000000ll;
mode->name = videomode->name;
mode->refresh = videomode->refresh;
mode->xres = videomode->xres;
mode->yres = videomode->yres;
do_div(div_result, videomode->pixclock);
mode->pixclock_freq = (u32)div_result;
mode->left_margin = videomode->left_margin;
mode->right_margin = videomode->right_margin;
mode->upper_margin = videomode->upper_margin;
mode->lower_margin = videomode->lower_margin;
mode->hsync_len = videomode->hsync_len;
mode->vsync_len = videomode->vsync_len;
mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT);
mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT);
/* no defined flag in fb, use vmode>>3*/
mode->invert_pixclock = !!(videomode->vmode & 8);
mode->pix_fmt_out = output_fmt;
}
static void mmpmode_to_fbmode(struct fb_videomode *videomode,
struct mmp_mode *mode)
{
u64 div_result = 1000000000000ll;
videomode->name = mode->name;
videomode->refresh = mode->refresh;
videomode->xres = mode->xres;
videomode->yres = mode->yres;
do_div(div_result, mode->pixclock_freq);
videomode->pixclock = (u32)div_result;
videomode->left_margin = mode->left_margin;
videomode->right_margin = mode->right_margin;
videomode->upper_margin = mode->upper_margin;
videomode->lower_margin = mode->lower_margin;
videomode->hsync_len = mode->hsync_len;
videomode->vsync_len = mode->vsync_len;
videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0)
| (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0);
videomode->vmode = mode->invert_pixclock ? 8 : 0;
}
static int mmpfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct mmpfb_info *fbi = info->par;
if (var->bits_per_pixel == 8)
return -EINVAL;
/*
* Basic geometry sanity checks.
*/
if (var->xoffset + var->xres > var->xres_virtual)
return -EINVAL;
if (var->yoffset + var->yres > var->yres_virtual)
return -EINVAL;
/*
* Check size of framebuffer.
*/
if (var->xres_virtual * var->yres_virtual *
(var->bits_per_pixel >> 3) > fbi->fb_size)
return -EINVAL;
return 0;
}
static unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset;
}
static u32 to_rgb(u16 red, u16 green, u16 blue)
{
red >>= 8;
green >>= 8;
blue >>= 8;
return (red << 16) | (green << 8) | blue;
}
static int mmpfb_setcolreg(unsigned int regno, unsigned int red,
unsigned int green, unsigned int blue,
unsigned int trans, struct fb_info *info)
{
struct mmpfb_info *fbi = info->par;
u32 val;
if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) {
val = chan_to_field(red, &info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue , &info->var.blue);
fbi->pseudo_palette[regno] = val;
}
if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) {
val = to_rgb(red, green, blue);
/* TODO */
}
return 0;
}
static int mmpfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct mmpfb_info *fbi = info->par;
struct mmp_addr addr;
memset(&addr, 0, sizeof(addr));
addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
* var->bits_per_pixel / 8 + fbi->fb_start_dma;
mmp_overlay_set_addr(fbi->overlay, &addr);
return 0;
}
static int var_update(struct fb_info *info)
{
struct mmpfb_info *fbi = info->par;
struct fb_var_screeninfo *var = &info->var;
struct fb_videomode *m;
int pix_fmt;
/* set pix_fmt */
pix_fmt = var_to_pixfmt(var);
if (pix_fmt < 0)
return -EINVAL;
pixfmt_to_var(var, pix_fmt);
fbi->pix_fmt = pix_fmt;
/* set var according to best video mode*/
m = (struct fb_videomode *)fb_match_mode(var, &info->modelist);
if (!m) {
dev_err(fbi->dev, "set par: no match mode, use best mode\n");
m = (struct fb_videomode *)fb_find_best_mode(var,
&info->modelist);
fb_videomode_to_var(var, m);
}
memcpy(&fbi->mode, m, sizeof(struct fb_videomode));
/* fix to 2* yres */
var->yres_virtual = var->yres * 2;
info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8;
info->fix.ypanstep = var->yres;
return 0;
}
static int mmpfb_set_par(struct fb_info *info)
{
struct mmpfb_info *fbi = info->par;
struct fb_var_screeninfo *var = &info->var;
struct mmp_addr addr;
struct mmp_win win;
struct mmp_mode mode;
int ret;
ret = var_update(info);
if (ret != 0)
return ret;
/* set window/path according to new videomode */
fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt);
mmp_path_set_mode(fbi->path, &mode);
memset(&win, 0, sizeof(win));
win.xsrc = win.xdst = fbi->mode.xres;
win.ysrc = win.ydst = fbi->mode.yres;
win.pix_fmt = fbi->pix_fmt;
mmp_overlay_set_win(fbi->overlay, &win);
/* set address always */
memset(&addr, 0, sizeof(addr));
addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset)
* var->bits_per_pixel / 8 + fbi->fb_start_dma;
mmp_overlay_set_addr(fbi->overlay, &addr);
return 0;
}
static void mmpfb_power(struct mmpfb_info *fbi, int power)
{
struct mmp_addr addr;
struct mmp_win win;
struct fb_var_screeninfo *var = &fbi->fb_info->var;
/* for power on, always set address/window again */
if (power) {
memset(&win, 0, sizeof(win));
win.xsrc = win.xdst = fbi->mode.xres;
win.ysrc = win.ydst = fbi->mode.yres;
win.pix_fmt = fbi->pix_fmt;
mmp_overlay_set_win(fbi->overlay, &win);
/* set address always */
memset(&addr, 0, sizeof(addr));
addr.phys[0] = fbi->fb_start_dma +
(var->yoffset * var->xres_virtual + var->xoffset)
* var->bits_per_pixel / 8;
mmp_overlay_set_addr(fbi->overlay, &addr);
}
mmp_overlay_set_onoff(fbi->overlay, power);
}
static int mmpfb_blank(int blank, struct fb_info *info)
{
struct mmpfb_info *fbi = info->par;
mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK));
return 0;
}
static struct fb_ops mmpfb_ops = {
.owner = THIS_MODULE,
.fb_blank = mmpfb_blank,
.fb_check_var = mmpfb_check_var,
.fb_set_par = mmpfb_set_par,
.fb_setcolreg = mmpfb_setcolreg,
.fb_pan_display = mmpfb_pan_display,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
static int modes_setup(struct mmpfb_info *fbi)
{
struct fb_videomode *videomodes;
struct mmp_mode *mmp_modes;
struct fb_info *info = fbi->fb_info;
int videomode_num, i;
/* get videomodes from path */
videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes);
if (!videomode_num) {
dev_warn(fbi->dev, "can't get videomode num\n");
return 0;
}
/* put videomode list to info structure */
videomodes = kzalloc(sizeof(struct fb_videomode) * videomode_num,
GFP_KERNEL);
if (!videomodes) {
dev_err(fbi->dev, "can't malloc video modes\n");
return -ENOMEM;
}
for (i = 0; i < videomode_num; i++)
mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]);
fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist);
/* set videomode[0] as default mode */
memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode));
fbi->output_fmt = mmp_modes[0].pix_fmt_out;
fb_videomode_to_var(&info->var, &fbi->mode);
mmp_path_set_mode(fbi->path, &mmp_modes[0]);
kfree(videomodes);
return videomode_num;
}
static int fb_info_setup(struct fb_info *info,
struct mmpfb_info *fbi)
{
int ret = 0;
/* Initialise static fb parameters.*/
info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK |
FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN;
info->node = -1;
strcpy(info->fix.id, fbi->name);
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.type_aux = 0;
info->fix.xpanstep = 0;
info->fix.ypanstep = info->var.yres;
info->fix.ywrapstep = 0;
info->fix.accel = FB_ACCEL_NONE;
info->fix.smem_start = fbi->fb_start_dma;
info->fix.smem_len = fbi->fb_size;
info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
info->fix.line_length = info->var.xres_virtual *
info->var.bits_per_pixel / 8;
info->fbops = &mmpfb_ops;
info->pseudo_palette = fbi->pseudo_palette;
info->screen_base = fbi->fb_start;
info->screen_size = fbi->fb_size;
/* For FB framework: Allocate color map and Register framebuffer*/
if (fb_alloc_cmap(&info->cmap, 256, 0) < 0)
ret = -ENOMEM;
return ret;
}
static void fb_info_clear(struct fb_info *info)
{
fb_dealloc_cmap(&info->cmap);
}
static int mmpfb_probe(struct platform_device *pdev)
{
struct mmp_buffer_driver_mach_info *mi;
struct fb_info *info = 0;
struct mmpfb_info *fbi = 0;
int ret, modes_num;
mi = pdev->dev.platform_data;
if (mi == NULL) {
dev_err(&pdev->dev, "no platform data defined\n");
return -EINVAL;
}
/* initialize fb */
info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev);
if (info == NULL)
return -ENOMEM;
fbi = info->par;
if (!fbi) {
ret = -EINVAL;
goto failed;
}
/* init fb */
fbi->fb_info = info;
platform_set_drvdata(pdev, fbi);
fbi->dev = &pdev->dev;
fbi->name = mi->name;
fbi->pix_fmt = mi->default_pixfmt;
pixfmt_to_var(&info->var, fbi->pix_fmt);
mutex_init(&fbi->access_ok);
/* get display path by name */
fbi->path = mmp_get_path(mi->path_name);
if (!fbi->path) {
dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name);
ret = -EINVAL;
goto failed_destroy_mutex;
}
dev_info(fbi->dev, "path %s get\n", fbi->path->name);
/* get overlay */
fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id);
if (!fbi->overlay) {
ret = -EINVAL;
goto failed_destroy_mutex;
}
/* set fetch used */
mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id);
modes_num = modes_setup(fbi);
if (modes_num < 0) {
ret = modes_num;
goto failed_destroy_mutex;
}
/*
* if get modes success, means not hotplug panels, use caculated buffer
* or use default size
*/
if (modes_num > 0) {
/* fix to 2* yres */
info->var.yres_virtual = info->var.yres * 2;
/* Allocate framebuffer memory: size = modes xy *4 */
fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual
* info->var.bits_per_pixel / 8;
} else {
fbi->fb_size = MMPFB_DEFAULT_SIZE;
}
fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size),
&fbi->fb_start_dma, GFP_KERNEL);
if (fbi->fb_start == NULL) {
dev_err(&pdev->dev, "can't alloc framebuffer\n");
ret = -ENOMEM;
goto failed_destroy_mutex;
}
memset(fbi->fb_start, 0, fbi->fb_size);
dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024);
/* fb power on */
if (modes_num > 0)
mmpfb_power(fbi, 1);
ret = fb_info_setup(info, fbi);
if (ret < 0)
goto failed_free_buff;
ret = register_framebuffer(info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register fb: %d\n", ret);
ret = -ENXIO;
goto failed_clear_info;
}
dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n",
info->node, info->fix.id);
#ifdef CONFIG_LOGO
if (fbi->fb_start) {
fb_prepare_logo(info, 0);
fb_show_logo(info, 0);
}
#endif
return 0;
failed_clear_info:
fb_info_clear(info);
failed_free_buff:
dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start,
fbi->fb_start_dma);
failed_destroy_mutex:
mutex_destroy(&fbi->access_ok);
failed:
dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n");
platform_set_drvdata(pdev, NULL);
framebuffer_release(info);
return ret;
}
static struct platform_driver mmpfb_driver = {
.driver = {
.name = "mmp-fb",
.owner = THIS_MODULE,
},
.probe = mmpfb_probe,
};
static int mmpfb_init(void)
{
return platform_driver_register(&mmpfb_driver);
}
module_init(mmpfb_init);
MODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>");
MODULE_DESCRIPTION("Framebuffer driver for Marvell displays");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,54 @@
/*
* linux/drivers/video/mmp/fb/mmpfb.h
* Framebuffer driver for Marvell Display controller.
*
* Copyright (C) 2012 Marvell Technology Group Ltd.
* Authors: Zhou Zhu <zzhu3@marvell.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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _MMP_FB_H_
#define _MMP_FB_H_
#include <video/mmp_disp.h>
#include <linux/fb.h>
/* LCD controller private state. */
struct mmpfb_info {
struct device *dev;
int id;
const char *name;
struct fb_info *fb_info;
/* basicaly videomode is for output */
struct fb_videomode mode;
int pix_fmt;
void *fb_start;
int fb_size;
dma_addr_t fb_start_dma;
struct mmp_overlay *overlay;
struct mmp_path *path;
struct mutex access_ok;
unsigned int pseudo_palette[16];
int output_fmt;
};
#define MMPFB_DEFAULT_SIZE (PAGE_ALIGN(1920 * 1080 * 4 * 2))
#endif /* _MMP_FB_H_ */

View file

@ -0,0 +1,20 @@
if MMP_DISP
config MMP_DISP_CONTROLLER
bool "mmp display controller hw support"
depends on CPU_PXA910 || CPU_MMP2 || CPU_MMP3 || CPU_PXA988
default n
help
Marvell MMP display hw controller support
this controller is used on Marvell PXA910,
MMP2, MMP3, PXA988 chips
config MMP_DISP_SPI
bool "mmp display controller spi port"
depends on MMP_DISP_CONTROLLER && SPI_MASTER
default y
help
Marvell MMP display hw controller spi port support
will register as a spi master for panel usage
endif

View file

@ -0,0 +1,2 @@
obj-$(CONFIG_MMP_DISP_CONTROLLER) += mmp_ctrl.o
obj-$(CONFIG_MMP_DISP_SPI) += mmp_spi.o

Some files were not shown because too many files have changed in this diff Show more