drivers 添加树莓派5寸屏驱动

This commit is contained in:
hejiawencc
2023-02-17 08:53:02 +08:00
parent 3b8018dc58
commit fca8ede037
11 changed files with 1288 additions and 0 deletions

View File

@ -192,4 +192,13 @@ config DRM_PANEL_SITRONIX_ST7789V
Say Y here if you want to enable support for the Sitronix Say Y here if you want to enable support for the Sitronix
ST7789V controller for 240x320 LCD panels ST7789V controller for 240x320 LCD panels
config DRM_PANEL_TOSHIBA_TC358762
tristate "support for toshiba tc358762"
depends on OF && I2C
depends on BACKLIGHT_CLASS_DEVICE
select VIDEOMODE_HELPERS
help
Say Y here if you want to enable support for toshiba tc358762 bridge.
To compile this driver as a module, choose M here.
endmenu endmenu

View File

@ -19,3 +19,4 @@ obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
obj-$(CONFIG_DRM_PANEL_TOSHIBA_TC358762) += panel-toshiba-tc358762.o

View File

@ -0,0 +1,621 @@
/*
* Copyright (C) 2013, NVIDIA Corporation. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sub license,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <linux/backlight.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
#include <video/display_timing.h>
#include <video/of_display_timing.h>
#include <video/videomode.h>
int trigger_bridge = 1;
struct panel_desc {
const struct drm_display_mode *modes;
unsigned int num_modes;
const struct display_timing *timings;
unsigned int num_timings;
unsigned int bpc;
struct {
unsigned int width;
unsigned int height;
} size;
/**
* @prepare: the time (in milliseconds) that it takes for the panel to
* become ready and start receiving video data
* @enable: the time (in milliseconds) that it takes for the panel to
* display the first valid frame after starting to receive
* video data
* @disable: the time (in milliseconds) that it takes for the panel to
* turn the display off (no content is visible)
* @unprepare: the time (in milliseconds) that it takes for the panel
* to power itself down completely
*/
struct {
unsigned int prepare;
unsigned int enable;
unsigned int disable;
unsigned int unprepare;
} delay;
u32 bus_format;
};
struct tc358762 {
struct drm_panel base;
bool prepared;
bool enabled;
struct device *dev;
struct mipi_dsi_device *dsi;
const struct panel_desc *desc;
struct backlight_device *backlight;
struct regulator *supply;
struct i2c_adapter *ddc;
struct gpio_desc *enable_gpio;
};
static inline struct tc358762 *to_tc358762(struct drm_panel *panel)
{
return container_of(panel, struct tc358762, base);
}
static int tc358762_get_fixed_modes(struct tc358762 *panel)
{
struct drm_connector *connector = panel->base.connector;
struct drm_device *drm = panel->base.drm;
struct drm_display_mode *mode;
unsigned int i, num = 0;
if (!panel->desc)
return 0;
for (i = 0; i < panel->desc->num_timings; i++) {
const struct display_timing *dt = &panel->desc->timings[i];
struct videomode vm;
videomode_from_timing(dt, &vm);
mode = drm_mode_create(drm);
if (!mode) {
dev_err(drm->dev, "failed to add mode %ux%u\n",
dt->hactive.typ, dt->vactive.typ);
continue;
}
drm_display_mode_from_videomode(&vm, mode);
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
num++;
}
for (i = 0; i < panel->desc->num_modes; i++) {
const struct drm_display_mode *m = &panel->desc->modes[i];
mode = drm_mode_duplicate(drm, m);
if (!mode) {
dev_err(drm->dev, "failed to add mode %ux%u@%u\n",
m->hdisplay, m->vdisplay, m->vrefresh);
continue;
}
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
num++;
}
connector->display_info.bpc = panel->desc->bpc;
connector->display_info.width_mm = panel->desc->size.width;
connector->display_info.height_mm = panel->desc->size.height;
if (panel->desc->bus_format)
drm_display_info_set_bus_formats(&connector->display_info,
&panel->desc->bus_format, 1);
return num;
}
static int tc358762_of_get_native_mode(struct tc358762 *panel)
{
struct drm_connector *connector = panel->base.connector;
struct drm_device *drm = panel->base.drm;
struct drm_display_mode *mode;
struct device_node *timings_np;
int ret;
u32 bus_flags;
timings_np = of_get_child_by_name(panel->dev->of_node,
"display-timings");
if (!timings_np) {
dev_dbg(panel->dev, "failed to find display-timings node\n");
return 0;
}
of_node_put(timings_np);
mode = drm_mode_create(drm);
if (!mode)
return 0;
ret = of_get_drm_display_mode(panel->dev->of_node, mode, &bus_flags, OF_USE_NATIVE_MODE);
if (ret) {
dev_dbg(panel->dev, "failed to find dts display timings\n");
drm_mode_destroy(drm, mode);
return 0;
}
drm_mode_set_name(mode);
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
return 1;
}
extern int rockpi_mcu_set_bright(int bright);
static int tc358762_disable(struct drm_panel *panel)
{
struct tc358762 *p = to_tc358762(panel);
if (!p->enabled)
return 0;
printk("panel disable\n");
rockpi_mcu_set_bright(0x00);
if (p->backlight) {
p->backlight->props.power = FB_BLANK_POWERDOWN;
backlight_update_status(p->backlight);
}
if (p->desc && p->desc->delay.disable)
msleep(p->desc->delay.disable);
p->enabled = false;
return 0;
}
static int tc358762_unprepare(struct drm_panel *panel)
{
struct tc358762 *p = to_tc358762(panel);
if (!p->prepared)
return 0;
if (p->enable_gpio)
gpiod_direction_output(p->enable_gpio, 0);
regulator_disable(p->supply);
if (p->desc && p->desc->delay.unprepare)
msleep(p->desc->delay.unprepare);
p->prepared = false;
return 0;
}
static void tc358762_gen_write(struct mipi_dsi_device *dsi, const void *data, size_t len)
{
int ret;
ret = mipi_dsi_generic_write(dsi, data, len);
if (ret < 0) {
dev_err(&dsi->dev, "failed to writing gen seq\n");
}
}
#define tc358762_gen_write_seq(dsi, seq...) \
({\
static const u8 d[] = { seq };\
tc358762_gen_write(dsi, d, ARRAY_SIZE(d));\
})
static int tc358762_dsi_init(struct tc358762 *p)
{
struct mipi_dsi_device *dsi = p->dsi;
tc358762_gen_write_seq(dsi, 0x10, 0x02, 0x03, 0x00, 0x00, 0x00);//LANE
tc358762_gen_write_seq(dsi, 0x64, 0x01, 0x0c, 0x00, 0x00, 0x00);//D0S_CLRSIPOCOUNT
tc358762_gen_write_seq(dsi, 0x68, 0x01, 0x0c, 0x00, 0x00, 0x00);//D1S_CLRSIPOCOUNT
tc358762_gen_write_seq(dsi, 0x44, 0x01, 0x00, 0x00, 0x00, 0x00);//D0S_ATMR
tc358762_gen_write_seq(dsi, 0x48, 0x01, 0x00, 0x00, 0x00, 0x00);//D1S_ATMR
tc358762_gen_write_seq(dsi, 0x14, 0x01, 0x15, 0x00, 0x00, 0x00);//LPTXTIMCNT
tc358762_gen_write_seq(dsi, 0x50, 0x04, 0x60, 0x00, 0x00, 0x00);//SPICMR/SPICTRL
tc358762_gen_write_seq(dsi, 0x20, 0x04, 0x52, 0x01, 0x10, 0x00);//PORT/LCDCTRL
tc358762_gen_write_seq(dsi, 0x24, 0x04, 0x14, 0x00, 0x1a, 0x00);//HBPR/HSR
tc358762_gen_write_seq(dsi, 0x28, 0x04, 0x20, 0x03, 0x69, 0x00);//HFPR/HDISP(*)
tc358762_gen_write_seq(dsi, 0x2c, 0x04, 0x02, 0x00, 0x15, 0x00);//VBFR/VSR
tc358762_gen_write_seq(dsi, 0x30, 0x04, 0xe0, 0x01, 0x07, 0x00);//VFPR/VDISP(*)
tc358762_gen_write_seq(dsi, 0x34, 0x04, 0x01, 0x00, 0x00, 0x00);//VFUEN
tc358762_gen_write_seq(dsi, 0x64, 0x04, 0x0f, 0x04, 0x00, 0x00);//SYSCTRL
tc358762_gen_write_seq(dsi, 0x04, 0x01, 0x01, 0x00, 0x00, 0x00);//STARTPPI
tc358762_gen_write_seq(dsi, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00);//STARTDSI
usleep_range(10, 20);
return 0;
}
static int tc358762_prepare(struct drm_panel *panel)
{
struct tc358762 *p = to_tc358762(panel);
int err;
if (p->prepared)
return 0;
err = regulator_enable(p->supply);
if (err < 0) {
dev_err(panel->dev, "failed to enable supply: %d\n", err);
return err;
}
if (p->enable_gpio)
gpiod_direction_output(p->enable_gpio, 1);
if (p->desc && p->desc->delay.prepare)
msleep(p->desc->delay.prepare);
p->prepared = true;
return 0;
}
extern void rockpi_mcu_screen_power_up(void);
static int tc358762_enable(struct drm_panel *panel)
{
struct tc358762 *p = to_tc358762(panel);
if (p->enabled)
return 0;
printk("panel enable\n");
if(trigger_bridge) {
pr_info("rockpi_mcu_screen_power_up");
rockpi_mcu_screen_power_up();
trigger_bridge = 0;
}
tc358762_dsi_init(p);
if (p->desc && p->desc->delay.enable)
msleep(p->desc->delay.enable);
if (p->backlight) {
p->backlight->props.power = FB_BLANK_UNBLANK;
backlight_update_status(p->backlight);
}
rockpi_mcu_set_bright(0xFF);
p->enabled = true;
return 0;
}
static int tc358762_get_modes(struct drm_panel *panel)
{
struct tc358762 *p = to_tc358762(panel);
int num = 0;
/* probe EDID if a DDC bus is available */
if (p->ddc) {
struct edid *edid = drm_get_edid(panel->connector, p->ddc);
drm_connector_update_edid_property(panel->connector, edid);
if (edid) {
num += drm_add_edid_modes(panel->connector, edid);
kfree(edid);
}
}
/* add hard-coded panel modes */
num += tc358762_get_fixed_modes(p);
/* add device node plane modes */
num += tc358762_of_get_native_mode(p);
return num;
}
static int tc358762_get_timings(struct drm_panel *panel,
unsigned int num_timings,
struct display_timing *timings)
{
struct tc358762 *p = to_tc358762(panel);
unsigned int i;
if (!p->desc)
return 0;
if (p->desc->num_timings < num_timings)
num_timings = p->desc->num_timings;
if (timings)
for (i = 0; i < num_timings; i++)
timings[i] = p->desc->timings[i];
return p->desc->num_timings;
}
static const struct drm_panel_funcs tc358762_funcs = {
.disable = tc358762_disable,
.unprepare = tc358762_unprepare,
.prepare = tc358762_prepare,
.enable = tc358762_enable,
.get_modes = tc358762_get_modes,
.get_timings = tc358762_get_timings,
};
static int tc358762_mipi_probe(struct mipi_dsi_device *dsi, const struct panel_desc *desc)
{
struct device_node *backlight, *ddc;
struct tc358762 *panel;
struct device *dev = &dsi->dev;
int err;
panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
if (!panel)
return -ENOMEM;
panel->enabled = false;
panel->prepared = false;
panel->desc = desc;
panel->dev = dev;
panel->dsi = dsi;
panel->supply = devm_regulator_get(dev, "power");
if (IS_ERR(panel->supply))
return PTR_ERR(panel->supply);
panel->enable_gpio = devm_gpiod_get_optional(dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(panel->enable_gpio)) {
err = PTR_ERR(panel->enable_gpio);
dev_err(dev, "failed to request GPIO: %d\n", err);
return err;
}
backlight = of_parse_phandle(dev->of_node, "backlight", 0);
if (backlight) {
panel->backlight = of_find_backlight_by_node(backlight);
of_node_put(backlight);
if (!panel->backlight)
return -EPROBE_DEFER;
}
ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
if (ddc) {
panel->ddc = of_find_i2c_adapter_by_node(ddc);
of_node_put(ddc);
if (!panel->ddc) {
err = -EPROBE_DEFER;
goto free_backlight;
}
}
drm_panel_init(&panel->base);
panel->base.dev = dev;
panel->base.funcs = &tc358762_funcs;
err = drm_panel_add(&panel->base);
if (err < 0)
goto free_ddc;
dev_set_drvdata(dev, panel);
return 0;
free_ddc:
if (panel->ddc)
put_device(&panel->ddc->dev);
free_backlight:
if (panel->backlight)
put_device(&panel->backlight->dev);
return err;
}
static int tc358762_remove(struct device *dev)
{
struct tc358762 *panel = dev_get_drvdata(dev);
drm_panel_detach(&panel->base);
drm_panel_remove(&panel->base);
tc358762_disable(&panel->base);
if (panel->ddc)
put_device(&panel->ddc->dev);
if (panel->backlight)
put_device(&panel->backlight->dev);
return 0;
}
static void tc358762_shutdown(struct device *dev)
{
struct tc358762 *panel = dev_get_drvdata(dev);
tc358762_disable(&panel->base);
}
struct bridge_desc {
struct panel_desc desc;
unsigned long flags;
enum mipi_dsi_pixel_format format;
unsigned int lanes;
};
static const struct drm_display_mode tc358762_mode = {
.clock = 26700,
.hdisplay = 800,
.hsync_start = 800 + 16,
.hsync_end = 800 + 16 + 4,
.htotal = 800 + 16 + 4 + 26,
.vdisplay = 480,
.vsync_start = 480 + 7,
.vsync_end = 480 + 7 + 2,
.vtotal = 480 + 7 + 2 + 21,
.vrefresh = 60,
.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
};
static const struct bridge_desc tc358762_bridge = {
.desc = {
.modes = &tc358762_mode,
.num_modes = 1,
.bpc = 8,
.size = {
.width = 217,
.height = 136,
},
},
.flags = MIPI_DSI_MODE_VIDEO |
MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_VIDEO_SYNC_PULSE,
.format = MIPI_DSI_FMT_RGB888,
.lanes = 1,
};
static const struct of_device_id dsi_of_match[] = {
{
.compatible = "rockpi,tc358762",
.data = &tc358762_bridge
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, dsi_of_match);
static int tc358762_dsi_probe(struct mipi_dsi_device *dsi)
{
const struct bridge_desc *desc;
const struct of_device_id *id;
const struct panel_desc *pdesc;
u32 val;
int err;
id = of_match_node(dsi_of_match, dsi->dev.of_node);
if (!id)
return -ENODEV;
desc = id->data;
printk("find panel: %s\n", id->compatible);
if (desc) {
dsi->mode_flags = desc->flags;
dsi->format = desc->format;
dsi->lanes = desc->lanes;
pdesc = &desc->desc;
} else {
pdesc = NULL;
}
err = tc358762_mipi_probe(dsi, pdesc);
if (err < 0)
return err;
if (!of_property_read_u32(dsi->dev.of_node, "dsi,flags", &val))
dsi->mode_flags = val;
if (!of_property_read_u32(dsi->dev.of_node, "dsi,format", &val))
dsi->format = val;
if (!of_property_read_u32(dsi->dev.of_node, "dsi,lanes", &val))
dsi->lanes = val;
return mipi_dsi_attach(dsi);
}
static int tc358762_dsi_remove(struct mipi_dsi_device *dsi)
{
int err;
err = mipi_dsi_detach(dsi);
if (err < 0)
dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err);
return tc358762_remove(&dsi->dev);
}
static void tc358762_dsi_shutdown(struct mipi_dsi_device *dsi)
{
tc358762_shutdown(&dsi->dev);
}
static struct mipi_dsi_driver tc358762_dsi_driver = {
.driver = {
.name = "bridge-tc358762-dsi",
.of_match_table = dsi_of_match,
},
.probe = tc358762_dsi_probe,
.remove = tc358762_dsi_remove,
.shutdown = tc358762_dsi_shutdown,
};
static int __init tc358762_init(void)
{
int err;
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
err = mipi_dsi_driver_register(&tc358762_dsi_driver);
if (err < 0)
return err;
}
return 0;
}
module_init(tc358762_init);
static void __exit tc358762_exit(void)
{
if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
mipi_dsi_driver_unregister(&tc358762_dsi_driver);
}
module_exit(tc358762_exit);
MODULE_AUTHOR("Jerry <xbl@rock-chips.com>");
MODULE_DESCRIPTION("DRM Driver for toshiba tc358762 Bridge");
MODULE_LICENSE("GPL and additional rights");

View File

@ -1409,6 +1409,12 @@ config TOUCHSCREEN_FT5436
Say Y here if you have Focaltech touch panel. Say Y here if you have Focaltech touch panel.
If unsure, say N. If unsure, say N.
config TOUCHSCREEN_ROCKPI_FT5406
tristate "rockpi ft5406"
depends on I2C
help
Control ft5406 touch ic.
source "drivers/input/touchscreen/cyttsp5/Kconfig" source "drivers/input/touchscreen/cyttsp5/Kconfig"
endif endif

View File

@ -127,4 +127,5 @@ obj-$(CONFIG_TOUCHSCREEN_GT1X) += gt1x/
obj-$(CONFIG_TOUCHSCREEN_HYN_CST2XX) += hyn_cst2xx/ obj-$(CONFIG_TOUCHSCREEN_HYN_CST2XX) += hyn_cst2xx/
obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/ obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/
obj-$(CONFIG_TOUCHSCREEN_FT5436) += focaltech_touch_ft5436/ obj-$(CONFIG_TOUCHSCREEN_FT5436) += focaltech_touch_ft5436/
obj-$(CONFIG_TOUCHSCREEN_ROCKPI_FT5406) += rockpi_ft5406.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) += cyttsp5/ obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) += cyttsp5/

View File

@ -0,0 +1,322 @@
/*
*
* rockpi BOARD FT5406 touch driver.
*
* Copyright (c) 2016 ASUSTek Computer Inc.
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* 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/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include "rockpi_ft5406.h"
static int fts_i2c_read(struct i2c_client *client, char *writebuf,
int writelen, char *readbuf, int readlen)
{
int ret;
if (writelen > 0) {
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = writelen,
.buf = writebuf,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = readlen,
.buf = readbuf,
},
};
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret < 0)
LOG_ERR("i2c read error, %d\n", ret);
} else {
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = readlen,
.buf = readbuf,
},
};
ret = i2c_transfer(client->adapter, msgs, 1);
if (ret < 0)
LOG_ERR("i2c read error, %d\n", ret);
}
return ret;
}
static int fts_read_reg(struct i2c_client *client, u8 addr, u8 *val)
{
return fts_i2c_read(client, &addr, 1, val, 1);
}
static int fts_check_fw_ver(struct i2c_client *client)
{
u8 reg_addr, fw_ver[3];
int ret;
reg_addr = FT_REG_FW_VER;
ret = fts_i2c_read(client, &reg_addr, 1, &fw_ver[0], 1);
if (ret < 0)
goto error;
reg_addr = FT_REG_FW_MIN_VER;
ret = fts_i2c_read(client, &reg_addr, 1, &fw_ver[1], 1);
if (ret < 0)
goto error;
reg_addr = FT_REG_FW_SUB_MIN_VER;
ret = fts_i2c_read(client, &reg_addr, 1, &fw_ver[2], 1);
if (ret < 0)
goto error;
LOG_INFO("Firmware version = %d.%d.%d\n", fw_ver[0], fw_ver[1], fw_ver[2]);
return 0;
error:
return ret;
}
static int fts_read_td_status(struct rockpi_ft5406_data *ts_data)
{
u8 td_status;
int ret = -1;
ret = fts_read_reg(ts_data->client, FT_TD_STATUS_REG, &td_status);
if (ret < 0) {
LOG_ERR("get reg td_status failed, %d\n", ret);
return ret;
}
return (int)td_status;
}
static int fts_read_touchdata(struct rockpi_ft5406_data *ts_data)
{
struct ts_event *event = &ts_data->event;
int ret = -1, i;
u8 buf[FT_ONE_TCH_LEN-2] = { 0 };
u8 reg_addr, pointid = FT_MAX_ID;
for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) {
reg_addr = FT_TOUCH_X_H_REG + (i * FT_ONE_TCH_LEN);
ret = fts_i2c_read(ts_data->client, &reg_addr, 1, buf, FT_ONE_TCH_LEN-2);
if (ret < 0) {
LOG_ERR("read touchdata failed.\n");
return ret;
}
pointid = (buf[FT_TOUCH_ID]) >> 4;
if (pointid >= MAX_TOUCH_POINTS)
break;
event->au8_finger_id[i] = pointid;
event->au16_x[i] = (s16) (buf[FT_TOUCH_X_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_X_L];
event->au16_y[i] = (s16) (buf[FT_TOUCH_Y_H] & 0x0F) << 8 | (s16) buf[FT_TOUCH_Y_L];
event->au8_touch_event[i] = buf[FT_TOUCH_EVENT] >> 6;
#if XY_REVERSE
event->au16_x[i] = SCREEN_WIDTH - event->au16_x[i] - 1;
event->au16_y[i] = SCREEN_HEIGHT - event->au16_y[i] - 1;
#endif
}
event->pressure = FT_PRESS;
return 0;
}
static void fts_report_value(struct rockpi_ft5406_data *ts_data)
{
struct ts_event *event = &ts_data->event;
int i, modified_ids = 0, released_ids;
for (i = 0; i < event->touch_point && i < MAX_TOUCH_POINTS; i++) {
if (event->au8_touch_event[i]== FT_TOUCH_DOWN
|| event->au8_touch_event[i] == FT_TOUCH_CONTACT)
{
modified_ids |= 1 << event->au8_finger_id[i];
input_mt_slot(ts_data->input_dev, event->au8_finger_id[i]);
input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER,
true);
input_report_abs(ts_data->input_dev, ABS_MT_TOUCH_MAJOR,
event->pressure);
input_report_abs(ts_data->input_dev, ABS_MT_POSITION_X,
event->au16_x[i]);
input_report_abs(ts_data->input_dev, ABS_MT_POSITION_Y,
event->au16_y[i]);
if(!((1 << event->au8_finger_id[i]) & ts_data->known_ids))
LOG_DBG("Touch id-%d: x = %d, y = %d\n",
event->au8_finger_id[i], event->au16_x[i], event->au16_y[i]);
}
}
released_ids = ts_data->known_ids & ~modified_ids;
for(i = 0; released_ids && i < MAX_TOUCH_POINTS; i++) {
if(released_ids & (1<<i)) {
LOG_DBG("Release id-%d, known = %x modified = %x\n", i, ts_data->known_ids, modified_ids);
input_mt_slot(ts_data->input_dev, i);
input_mt_report_slot_state(ts_data->input_dev, MT_TOOL_FINGER, false);
modified_ids &= ~(1 << i);
}
}
ts_data->known_ids = modified_ids;
input_mt_report_pointer_emulation(ts_data->input_dev, true);
input_sync(ts_data->input_dev);
}
extern int rockpi_mcu_is_connected(void);
static void rockpi_ft5406_work(struct work_struct *work)
{
struct rockpi_ft5406_data *ts_data
= container_of(work, struct rockpi_ft5406_data, ft5406_work);
struct ts_event *event = &ts_data->event;
int ret = 0, count = 8, td_status;
while(count > 0) {
ret = fts_check_fw_ver(ts_data->client);
if (ret == 0)
break;
LOG_INFO("checking touch ic, countdown: %d\n", count);
msleep(1000);
count--;
}
if (!count) {
LOG_ERR("checking touch ic timeout, %d\n", ret);
return;
}
//polling 60fps
while(1) {
td_status = fts_read_td_status(ts_data);
if (td_status < VALID_TD_STATUS_VAL+1 && (td_status > 0 || ts_data->known_ids != 0)) {
memset(event, -1, sizeof(struct ts_event));
event->touch_point = td_status;
ret = fts_read_touchdata(ts_data);
if (ret == 0)
fts_report_value(ts_data);
}
msleep_interruptible(17);
}
}
static int rockpi_ft5406_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct rockpi_ft5406_data *ts_data;
struct input_dev *input_dev;
int ret = 0, timeout = 10;
LOG_INFO("address = 0x%x\n", client->addr);
ts_data = kzalloc(sizeof(struct rockpi_ft5406_data), GFP_KERNEL);
if (ts_data == NULL) {
LOG_ERR("no memory for device\n");
return -ENOMEM;
}
ts_data->client = client;
i2c_set_clientdata(client, ts_data);
while(!rockpi_mcu_is_connected() && timeout > 0) {
msleep(50);
timeout--;
}
if (timeout == 0) {
LOG_ERR("wait connected timeout\n");
ret = -ENODEV;
goto timeout_failed;
}
input_dev = input_allocate_device();
if (!input_dev) {
LOG_ERR("failed to allocate input device\n");
goto input_allocate_failed;
}
input_dev->name = "fts_ts";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &ts_data->client->dev;
ts_data->input_dev = input_dev;
input_set_drvdata(input_dev, ts_data);
__set_bit(EV_SYN, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
input_mt_init_slots(input_dev, MAX_TOUCH_POINTS, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
SCREEN_WIDTH, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
SCREEN_HEIGHT, 0, 0);
ret = input_register_device(input_dev);
if (ret) {
LOG_ERR("Input device registration failed\n");
goto input_register_failed;
}
INIT_WORK(&ts_data->ft5406_work, rockpi_ft5406_work);
schedule_work(&ts_data->ft5406_work);
return 0;
input_register_failed:
input_free_device(input_dev);
input_allocate_failed:
timeout_failed:
kfree(ts_data);
return ret;
}
static int rockpi_ft5406_remove(struct i2c_client *client)
{
struct rockpi_ft5406_data *ts_data = i2c_get_clientdata(client);
cancel_work_sync(&ts_data->ft5406_work);
if (ts_data->input_dev) {
input_unregister_device(ts_data->input_dev);
input_free_device(ts_data->input_dev);
}
kfree(ts_data);
return 0;
}
static const struct i2c_device_id rockpi_ft5406_id[] = {
{"rockpi_ft5406", 0},
{},
};
static struct i2c_driver rockpi_ft5406_driver = {
.driver = {
.name = "rockpi_ft5406",
},
.probe = rockpi_ft5406_probe,
.remove = rockpi_ft5406_remove,
.id_table = rockpi_ft5406_id,
};
module_i2c_driver(rockpi_ft5406_driver);
MODULE_DESCRIPTION("ROCKPI BOARD FT5406 Touch driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,63 @@
#ifndef _ROCKPI_FT5406_H_
#define _ROCKPI_FT5406_H_
#define LOG_DBG(fmt,arg...) pr_debug("rockpi-ft5406: %s: "fmt, __func__, ##arg);
#define LOG_INFO(fmt,arg...) pr_info("rockpi-ft5406: %s: "fmt, __func__, ##arg);
#define LOG_ERR(fmt,arg...) pr_err("rockpi-ft5406: %s: "fmt, __func__, ##arg);
#define XY_REVERSE 1
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 480
#define FT_ONE_TCH_LEN 6
#define FT_REG_FW_VER 0xA6
#define FT_REG_FW_MIN_VER 0xB2
#define FT_REG_FW_SUB_MIN_VER 0xB3
#define VALID_TD_STATUS_VAL 10
#define MAX_TOUCH_POINTS 1
#define FT_PRESS 0x7F
#define FT_MAX_ID 0x0F
#define FT_TOUCH_X_H 0
#define FT_TOUCH_X_L 1
#define FT_TOUCH_Y_H 2
#define FT_TOUCH_Y_L 3
#define FT_TOUCH_EVENT 0
#define FT_TOUCH_ID 2
#define FT_TOUCH_X_H_REG 3
#define FT_TOUCH_X_L_REG 4
#define FT_TOUCH_Y_H_REG 5
#define FT_TOUCH_Y_L_REG 6
#define FT_TD_STATUS_REG 2
#define FT_TOUCH_EVENT_REG 3
#define FT_TOUCH_ID_REG 5
#define FT_TOUCH_DOWN 0
#define FT_TOUCH_CONTACT 2
struct ts_event {
u16 au16_x[MAX_TOUCH_POINTS]; /*x coordinate */
u16 au16_y[MAX_TOUCH_POINTS]; /*y coordinate */
u8 au8_touch_event[MAX_TOUCH_POINTS]; /*touch event: 0:down; 1:up; 2:contact */
u8 au8_finger_id[MAX_TOUCH_POINTS]; /*touch ID */
u16 pressure;
u8 touch_point;
u8 point_num;
};
struct rockpi_ft5406_data {
struct device *dev;
struct i2c_client *client;
struct input_dev *input_dev;
struct ts_event event;
struct work_struct ft5406_work;
int known_ids;
};
#endif

View File

@ -548,6 +548,14 @@ config RK803
help help
Driver for RK803 which is used for driving porjector and IR flood LED. Driver for RK803 which is used for driving porjector and IR flood LED.
config ROCKPI_MCU
tristate "rockpi mcu"
default n
depends on I2C
help
Control the power of touch screen for rockpi board.
source "drivers/misc/c2port/Kconfig" source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig" source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig" source "drivers/misc/cb710/Kconfig"

View File

@ -62,3 +62,4 @@ obj-$(CONFIG_MISC_RTSX) += cardreader/
obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o
obj-$(CONFIG_PIR_ASCHIP) += pir-aschip.o obj-$(CONFIG_PIR_ASCHIP) += pir-aschip.o
obj-$(CONFIG_RK803) += rk803.o obj-$(CONFIG_RK803) += rk803.o
obj-$(CONFIG_ROCKPI_MCU) += rockpi_mcu.o

242
drivers/misc/rockpi_mcu.c Normal file
View File

@ -0,0 +1,242 @@
/*
*
* Rockpi board Touchscreen MCU driver.
*
* Copyright (c) 2016 ASUSTek Computer Inc.
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
*
* 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/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include "rockpi_mcu.h"
static struct rockpi_mcu_data *g_mcu_data;
static int connected = 0;
static int is_hex(char num)
{
//0-9, a-f, A-F
if ((47 < num && num < 58) || (64 < num && num < 71) || (96 < num && num < 103))
return 1;
return 0;
}
static int string_to_byte(const char *source, unsigned char *destination, int size)
{
int i = 0, counter = 0;
char c[3] = {0};
unsigned char bytes;
if (size%2 == 1)
return -EINVAL;
for(i = 0; i < size; i++){
if(!is_hex(source[i])) {
return -EINVAL;
}
if(0 == i%2){
c[0] = source[i];
c[1] = source[i+1];
sscanf(c, "%hhx", &bytes);
destination[counter] = bytes;
counter++;
}
}
return 0;
}
static int send_cmds(struct i2c_client *client, const char *buf)
{
int ret, size = strlen(buf);
unsigned char byte_cmd[size/2];
if ((size%2) != 0) {
LOG_ERR("size should be even\n");
return -EINVAL;
}
LOG_INFO("%s\n", buf);
string_to_byte(buf, byte_cmd, size);
ret = i2c_master_send(client, byte_cmd, size/2);
if (ret <= 0) {
//LOG_ERR("send command failed, ret = %d\n", ret);
printk("send command failed, ret = %d\n", ret);
return ret!=0 ? ret : -ECOMM;
}
msleep(20);
return 0;
}
static int recv_cmds(struct i2c_client *client, char *buf, int size)
{
int ret;
ret = i2c_master_recv(client, buf, size);
if (ret <= 0) {
LOG_ERR("receive commands failed, %d\n", ret);
return ret!=0 ? ret : -ECOMM;
}
msleep(20);
return 0;
}
static int init_cmd_check(struct rockpi_mcu_data *mcu_data)
{
int ret;
char recv_buf[1] = {0};
ret = send_cmds(mcu_data->client, "80");
if (ret < 0)
goto error;
recv_cmds(mcu_data->client, recv_buf, 1);
if (ret < 0)
goto error;
LOG_INFO("recv_cmds: 0x%X\n", recv_buf[0]);
if (recv_buf[0] != 0xDE && recv_buf[0] != 0xC3) {
LOG_ERR("read wrong info\n");
ret = -EINVAL;
goto error;
}
return 0;
error:
return ret;
}
int rockpi_mcu_screen_power_up(void)
{
int res = 0;
if (!connected)
return -ENODEV;
LOG_INFO("\n");
res = send_cmds(g_mcu_data->client, "8500");
if(res < 0)
printk("send 8500 failed\n");
msleep(800);
res = send_cmds(g_mcu_data->client, "8501");
if(res < 0)
printk("send 8501 failed\n");
msleep(800);
res = send_cmds(g_mcu_data->client, "8104");
if(res < 0)
printk("send 8104 failed\n");
return 0;
}
EXPORT_SYMBOL_GPL(rockpi_mcu_screen_power_up);
int rockpi_mcu_set_bright(int bright)
{
unsigned char cmd[2];
int ret;
if (!connected)
return -ENODEV;
if (bright > 0xff || bright < 0)
return -EINVAL;
LOG_INFO("bright = 0x%x\n", bright);
cmd[0] = 0x86;
cmd[1] = bright;
ret = i2c_master_send(g_mcu_data->client, cmd, 2);
if (ret <= 0) {
LOG_ERR("send command failed, ret = %d\n", ret);
return ret != 0 ? ret : -ECOMM;
}
return 0;
}
EXPORT_SYMBOL_GPL(rockpi_mcu_set_bright);
int rockpi_mcu_is_connected(void)
{
return connected;
}
EXPORT_SYMBOL_GPL(rockpi_mcu_is_connected);
static int rockpi_mcu_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct rockpi_mcu_data *mcu_data;
int ret;
LOG_INFO("address = 0x%x\n", client->addr);
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
LOG_ERR("I2C check functionality failed\n");
return -ENODEV;
}
mcu_data = kzalloc(sizeof(struct rockpi_mcu_data), GFP_KERNEL);
if (mcu_data == NULL) {
LOG_ERR("no memory for device\n");
return -ENOMEM;
}
mcu_data->client = client;
i2c_set_clientdata(client, mcu_data);
g_mcu_data = mcu_data;
ret = init_cmd_check(mcu_data);
if (ret < 0) {
LOG_ERR("init_cmd_check failed, %d\n", ret);
goto error;
}
connected = 1;
return 0;
error:
kfree(mcu_data);
return ret;
}
static int rockpi_mcu_remove(struct i2c_client *client)
{
struct rockpi_mcu_data *mcu_data = i2c_get_clientdata(client);
connected = 0;
kfree(mcu_data);
return 0;
}
static const struct i2c_device_id rockpi_mcu_id[] = {
{"rockpi_mcu", 0},
{},
};
static struct i2c_driver rockpi_mcu_driver = {
.driver = {
.name = "rockpi_mcu",
},
.probe = rockpi_mcu_probe,
.remove = rockpi_mcu_remove,
.id_table = rockpi_mcu_id,
};
module_i2c_driver(rockpi_mcu_driver);
MODULE_DESCRIPTION("rockpi Board TouchScreen MCU driver");
MODULE_LICENSE("GPL v2");

14
drivers/misc/rockpi_mcu.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef _ROCKPI_MCU_H_
#define _ROCKPI_MCU_H_
#define LOG_INFO(fmt,arg...) pr_info("rockpi-mcu: %s: "fmt, __func__, ##arg);
#define LOG_ERR(fmt,arg...) pr_err("rockpi-mcu: %s: "fmt, __func__, ##arg);
#define MAX_I2C_LEN 255
struct rockpi_mcu_data {
struct device *dev;
struct i2c_client *client;
};
#endif