drivers 添加树莓派5寸屏驱动
This commit is contained in:
@ -192,4 +192,13 @@ config DRM_PANEL_SITRONIX_ST7789V
|
||||
Say Y here if you want to enable support for the Sitronix
|
||||
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
|
||||
|
||||
@ -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_LS043T1LE01) += panel-sharp-ls043t1le01.o
|
||||
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
|
||||
obj-$(CONFIG_DRM_PANEL_TOSHIBA_TC358762) += panel-toshiba-tc358762.o
|
||||
|
||||
621
drivers/gpu/drm/panel/panel-toshiba-tc358762.c
Normal file
621
drivers/gpu/drm/panel/panel-toshiba-tc358762.c
Normal 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");
|
||||
@ -1409,6 +1409,12 @@ config TOUCHSCREEN_FT5436
|
||||
Say Y here if you have Focaltech touch panel.
|
||||
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"
|
||||
|
||||
endif
|
||||
|
||||
@ -127,4 +127,5 @@ obj-$(CONFIG_TOUCHSCREEN_GT1X) += gt1x/
|
||||
obj-$(CONFIG_TOUCHSCREEN_HYN_CST2XX) += hyn_cst2xx/
|
||||
obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/
|
||||
obj-$(CONFIG_TOUCHSCREEN_FT5436) += focaltech_touch_ft5436/
|
||||
obj-$(CONFIG_TOUCHSCREEN_ROCKPI_FT5406) += rockpi_ft5406.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) += cyttsp5/
|
||||
|
||||
322
drivers/input/touchscreen/rockpi_ft5406.c
Normal file
322
drivers/input/touchscreen/rockpi_ft5406.c
Normal 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, ®_addr, 1, &fw_ver[0], 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
reg_addr = FT_REG_FW_MIN_VER;
|
||||
ret = fts_i2c_read(client, ®_addr, 1, &fw_ver[1], 1);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
reg_addr = FT_REG_FW_SUB_MIN_VER;
|
||||
ret = fts_i2c_read(client, ®_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, ®_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");
|
||||
63
drivers/input/touchscreen/rockpi_ft5406.h
Normal file
63
drivers/input/touchscreen/rockpi_ft5406.h
Normal 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
|
||||
@ -548,6 +548,14 @@ config RK803
|
||||
help
|
||||
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/eeprom/Kconfig"
|
||||
source "drivers/misc/cb710/Kconfig"
|
||||
|
||||
@ -62,3 +62,4 @@ obj-$(CONFIG_MISC_RTSX) += cardreader/
|
||||
obj-$(CONFIG_UID_SYS_STATS) += uid_sys_stats.o
|
||||
obj-$(CONFIG_PIR_ASCHIP) += pir-aschip.o
|
||||
obj-$(CONFIG_RK803) += rk803.o
|
||||
obj-$(CONFIG_ROCKPI_MCU) += rockpi_mcu.o
|
||||
|
||||
242
drivers/misc/rockpi_mcu.c
Normal file
242
drivers/misc/rockpi_mcu.c
Normal 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
14
drivers/misc/rockpi_mcu.h
Normal 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
|
||||
Reference in New Issue
Block a user