/*
* Copyright (C) 2013 Noralf Tronnes
*
* This driver is inspired by:
* st7735fb.c, Copyright (C) 2011, Matt Porter
* broadsheetfb.c, Copyright (C) 2008, Jaya Kumar
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/backlight.h>
#include "fbtft.h"
extern void fbtft_sysfs_init(struct fbtft_par *par);
extern void fbtft_sysfs_exit(struct fbtft_par *par);
extern int fbtft_gamma_parse_str(struct fbtft_par *par, unsigned long *curves, const char *str, int size);
// This will be used if the driver doesn't provide debug support
#ifdef DEBUG
static unsigned long dummy_debug = 0xFFFFFFFF;
#else
static unsigned long dummy_debug = 0;
#endif
void _fbtft_dev_dbg_hex(const struct device *dev, int groupsize, void *buf, size_t len, const char *fmt, ...)
{
va_list args;
static char textbuf[512];
char *text = textbuf;
size_t text_len;
va_start(args, fmt);
text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
va_end(args);
hex_dump_to_buffer(buf, len, 32, groupsize, text + text_len, 512 - text_len, false);
if (len > 32)
dev_info(dev, "%s ...\n", text);
else
dev_info(dev, "%s\n", text);
}
unsigned long fbtft_request_gpios_match(struct fbtft_par *par, const struct fbtft_gpio *gpio)
{
int ret;
long val;
fbtft_fbtft_dev_dbg(DEBUG_REQUEST_GPIOS_MATCH, par, par->info->device, "%s('%s')\n", __func__, gpio->name);
if (strcasecmp(gpio->name, "reset") == 0) {
par->gpio.reset = gpio->gpio;
return GPIOF_OUT_INIT_HIGH;
}
else if (strcasecmp(gpio->name, "dc") == 0) {
par->gpio.dc = gpio->gpio;
return GPIOF_OUT_INIT_LOW;
}
else if (strcasecmp(gpio->name, "cs") == 0) {
par->gpio.cs = gpio->gpio;
return GPIOF_OUT_INIT_HIGH;
}
else if (strcasecmp(gpio->name, "wr") == 0) {
par->gpio.wr = gpio->gpio;
return GPIOF_OUT_INIT_HIGH;
}
else if (strcasecmp(gpio->name, "rd") == 0) {
par->gpio.rd = gpio->gpio;
return GPIOF_OUT_INIT_HIGH;
}
else if (strcasecmp(gpio->name, "latch") == 0) {
par->gpio.latch = gpio->gpio;
return GPIOF_OUT_INIT_LOW;
}
else if (gpio->name[0] == 'd' && gpio->name[1] == 'b') {
ret = kstrtol(&gpio->name[2], 10, &val);
if (ret == 0 && val < 16) {
par->gpio.db[val] = gpio->gpio;
return GPIOF_OUT_INIT_LOW;
}
}
else if (strcasecmp(gpio->name, "led") == 0) {
par->gpio.led[0] = gpio->gpio;
return GPIOF_OUT_INIT_LOW;
}
return FBTFT_GPIO_NO_MATCH;
}
int fbtft_request_gpios(struct fbtft_par *par)
{
struct fbtft_platform_data *pdata = par->pdata;
const struct fbtft_gpio *gpio;
unsigned long flags;
int i;
int ret;
/* Initialize gpios to disabled */
par->gpio.reset = -1;
par->gpio.dc = -1;
par->gpio.rd = -1;
par->gpio.wr = -1;
par->gpio.cs = -1;
par->gpio.latch = -1;
for (i=0;i<16;i++) {
par->gpio.db[i] = -1;
par->gpio.led[i] = -1;
par->gpio.aux[i] = -1;
}
if (pdata && pdata->gpios) {
gpio = pdata->gpios;
while (gpio->name[0]) {
flags = FBTFT_GPIO_NO_MATCH;
/* if driver provides match function, try it first, if no match use our own */
if (par->fbtftops.request_gpios_match)
flags = par->fbtftops.request_gpios_match(par, gpio);
if (flags == FBTFT_GPIO_NO_MATCH)
flags = fbtft_request_gpios_match(par, gpio);
if (flags != FBTFT_GPIO_NO_MATCH) {
ret = gpio_request_one(gpio->gpio, flags, par->info->device->driver->name);
if (ret < 0) {
dev_err(par->info->device, "%s: gpio_request_one('%s'=%d) failed with %d\n", __func__, gpio->name, gpio->gpio, ret);
return ret;
}
fbtft_fbtft_dev_dbg(DEBUG_REQUEST_GPIOS, par, par->info->device, "%s: '%s' = GPIO%d\n", __func__, gpio->name, gpio->gpio);
}
gpio++;
}
}
return 0;
}
void fbtft_free_gpios(struct fbtft_par *par)
{
struct fbtft_platform_data *pdata = NULL;
const struct fbtft_gpio *gpio;
fbtft_fbtft_dev_dbg(DEBUG_FREE_GPIOS, par, par->info->device, "%s()\n", __func__);
if(par->spi)
pdata = par->spi->dev.platform_data;
if (par->pdev)
pdata = par->pdev->dev.platform_data;
if (pdata && pdata->gpios) {
gpio = pdata->gpios;
while (gpio->name[0]) {
fbtft_fbtft_dev_dbg(DEBUG_FREE_GPIOS, par, par->info->device, "%s(): gpio_free('%s'=%d)\n", __func__, gpio->name, gpio->gpio);
gpio_direction_input(gpio->gpio); /* if the gpio wasn't recognized by request_gpios, WARN() will protest */
gpio_free(gpio->gpio);
gpio++;
}
}
}
int fbtft_backlight_update_status(struct backlight_device *bd)
{
struct fbtft_par *par = bl_get_data(bd);
bool polarity = !!(bd->props.state & BL_CORE_DRIVER1);
fbtft_fbtft_dev_dbg(DEBUG_BACKLIGHT, par, par->info->device, "%s: polarity=%d, power=%d, fb_blank=%d\n", __func__, polarity, bd->props.power, bd->props.fb_blank);
if ((bd->props.power == FB_BLANK_UNBLANK) && (bd->props.fb_blank == FB_BLANK_UNBLANK))
gpio_set_value(par->gpio.led[0], polarity);
else
gpio_set_value(par->gpio.led[0], !polarity);
return 0;
}
int fbtft_backlight_get_brightness(struct backlight_device *bd)
{
return bd->props.brightness;
}
void fbtft_unregister_backlight(struct fbtft_par *par)
{
const struct backlight_ops *bl_ops;
fbtft_fbtft_dev_dbg(DEBUG_BACKLIGHT, par, par->info->device, "%s()\n", __func__);
if (par->info->bl_dev) {
bl_ops = par->info->bl_dev->ops;
backlight_device_unregister(par->info->bl_dev);
par->info->bl_dev = NULL;
kfree(bl_ops);
}
}
EXPORT_SYMBOL(fbtft_unregister_backlight);
void fbtft_register_backlight(struct fbtft_par *par)
{
struct backlight_device *bd;
struct backlight_properties bl_props = { 0, };
struct backlight_ops *bl_ops;
fbtft_fbtft_dev_dbg(DEBUG_BACKLIGHT, par, par->info->device, "%s()\n", __func__);
if (par->gpio.led[0] == -1) {
fbtft_fbtft_dev_dbg(DEBUG_BACKLIGHT, par, par->info->device, "%s(): led pin not set, exiting.\n", __func__);
return;
}
bl_ops = kzalloc(sizeof(struct backlight_ops), GFP_KERNEL);
if (!bl_ops) {
dev_err(par->info->device, "%s: could not allocate memeory for backlight operations.\n", __func__);
return;
}
bl_ops->get_brightness = fbtft_backlight_get_brightness;
bl_ops->update_status = fbtft_backlight_update_status;
bl_props.type = BACKLIGHT_RAW;
/* Assume backlight is off, get polarity from current state of pin */
bl_props.power = FB_BLANK_POWERDOWN;
if (!gpio_get_value(par->gpio.led[0]))
bl_props.state |= BL_CORE_DRIVER1;
bd = backlight_device_register(dev_driver_string(par->info->device), par->info->device, par, bl_ops, &bl_props);
if (IS_ERR(bd)) {
dev_err(par->info->device, "cannot register backlight device (%ld)\n", PTR_ERR(bd));
goto failed;
}
par->info->bl_dev = bd;
if (!par->fbtftops.unregister_backlight)
par->fbtftops.unregister_backlight = fbtft_unregister_backlight;
return;
failed:
if (bl_ops)
kfree(bl_ops);
}
EXPORT_SYMBOL(fbtft_register_backlight);
void fbtft_set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
{
fbtft_fbtft_dev_dbg(DEBUG_SET_ADDR_WIN, par, par->info->device, "%s(xs=%d, ys=%d, xe=%d, ye=%d)\n", __func__, xs, ys, xe, ye);
/* Column address set */
write_cmd(par, 0x2A);
write_data(par, (xs >> 8) & 0xF
- 1
- 2
前往页