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:
commit
7c2db36e73
165 changed files with 9681 additions and 1307 deletions
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
------------------------
|
------------------------
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
----------------------
|
----------------------
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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[] = {
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
338
drivers/rtc/rtc-lp8788.c
Normal 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
641
drivers/rtc/rtc-max77686.c
Normal 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");
|
||||||
|
|
@ -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
552
drivers/rtc/rtc-max8997.c
Normal 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");
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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
314
drivers/rtc/rtc-rx4581.c
Normal 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");
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
349
drivers/rtc/rtc-tps80031.c
Normal 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");
|
||||||
|
|
@ -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),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
380
drivers/video/backlight/as3711_bl.c
Normal file
380
drivers/video/backlight/as3711_bl.c
Normal 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");
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
497
drivers/video/backlight/hx8357.c
Normal file
497
drivers/video/backlight/hx8357.c
Normal 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");
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
441
drivers/video/backlight/lms501kf03.c
Normal file
441
drivers/video/backlight/lms501kf03.c
Normal 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");
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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 = {
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
318
drivers/video/goldfishfb.c
Normal 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
11
drivers/video/mmp/Kconfig
Normal 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
|
||||||
1
drivers/video/mmp/Makefile
Normal file
1
drivers/video/mmp/Makefile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
obj-y += core.o hw/ panel/ fb/
|
||||||
258
drivers/video/mmp/core.c
Normal file
258
drivers/video/mmp/core.c
Normal 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);
|
||||||
13
drivers/video/mmp/fb/Kconfig
Normal file
13
drivers/video/mmp/fb/Kconfig
Normal 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
|
||||||
1
drivers/video/mmp/fb/Makefile
Normal file
1
drivers/video/mmp/fb/Makefile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
obj-$(CONFIG_MMP_FB) += mmpfb.o
|
||||||
685
drivers/video/mmp/fb/mmpfb.c
Normal file
685
drivers/video/mmp/fb/mmpfb.c
Normal 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");
|
||||||
54
drivers/video/mmp/fb/mmpfb.h
Normal file
54
drivers/video/mmp/fb/mmpfb.h
Normal 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_ */
|
||||||
20
drivers/video/mmp/hw/Kconfig
Normal file
20
drivers/video/mmp/hw/Kconfig
Normal 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
|
||||||
2
drivers/video/mmp/hw/Makefile
Normal file
2
drivers/video/mmp/hw/Makefile
Normal 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
Loading…
Add table
Add a link
Reference in a new issue