blob: f910733ba6e79f68249099a2f618b6ee03d6970f [file]
/* SPDX-License-Identifier: MIT */
#include "gs_panel/gs_panel_test.h"
/* Registers */
static const u8 test_key_enable[] = { 0xF0, 0x5A, 0x5A };
static const u8 test_key_disable[] = { 0xF0, 0xA5, 0xA5 };
const struct gs_dsi_cmd irc_read_cmds[] = {
GS_DSI_CMD(0xB0, 0x00, 0x03, 0x68),
};
static DEFINE_GS_CMDSET(irc_read);
const struct gs_dsi_cmd fgz_read_cmds[] = {
GS_DSI_CMD(0xB0, 0x00, 0x61, 0x68),
};
static DEFINE_GS_CMDSET(fgz_read);
const struct gs_dsi_cmd opr_read_cmds[] = {
GS_DSI_CMD(0xB0, 0x00, 0xD8, 0x63),
};
static DEFINE_GS_CMDSET(opr_read);
const struct gs_dsi_cmd test_key_enable_cmds[] = {
GS_DSI_CMDLIST(test_key_enable),
};
static DEFINE_GS_CMDSET(test_key_enable);
const struct gs_dsi_cmd test_key_disable_cmds[] = {
GS_DSI_CMDLIST(test_key_disable),
};
static DEFINE_GS_CMDSET(test_key_disable);
/* sorted by register address */
static const struct gs_panel_register tk4c_registers[] = {
GS_PANEL_REG("lp_status", 0x54),
GS_PANEL_REG_LONG_WITH_CMDS("opr", 0x63, 2, &opr_read_cmdset),
GS_PANEL_REG_WITH_CMDS("irc", 0x68, &irc_read_cmdset),
GS_PANEL_REG_LONG_WITH_CMDS("fgz", 0x68, 8, &fgz_read_cmdset),
GS_PANEL_REG_LONG("refresh_rate", 0x84, 2),
};
static const struct gs_panel_registers_desc tk4c_reg_desc = {
.register_count = ARRAY_SIZE(tk4c_registers),
.registers = tk4c_registers,
.global_pre_read_cmdset = &test_key_enable_cmdset,
.global_post_read_cmdset = &test_key_disable_cmdset,
};
/* Query functions */
static bool array_is_equal(const u8 *l, const u8 *r, int count)
{
return memcmp(l, r, count * sizeof(u8)) == 0;
}
struct array_to_value {
const u8 *array;
const int value;
const u64 rev;
};
struct gs_panel_register_query {
const struct gs_panel_register *reg;
const struct array_to_value *map;
int map_size;
int default_result;
};
int get_query_result_from_register(struct gs_panel_test *test,
const struct gs_panel_register_query *query)
{
struct gs_panel *ctx = test->ctx;
int i, ret = -1;
u8 *read_result;
if (!query || !query->reg)
return ret;
read_result = kmalloc_array(query->reg->size, sizeof(u8), GFP_KERNEL);
if (gs_panel_read_register_value(test, query->reg, read_result))
goto free_mem;
ret = query->default_result;
for (i = 0; i < query->map_size; i++) {
if (query->map[i].rev && !(ctx->panel_rev & query->map[i].rev))
continue;
if (array_is_equal(read_result, query->map[i].array, query->reg->size)) {
ret = query->map[i].value;
goto free_mem;
}
}
free_mem:
kfree(read_result);
return ret;
}
int tk4c_query_is_aod_on(struct gs_panel_test *test)
{
static const u8 aod_on_value[] = { 0x24 };
static struct array_to_value aod_read_map[] = {
{ .array = aod_on_value, .value = 1 },
};
const struct gs_panel_register_query aod_query = {
.reg = &tk4c_registers[0],
.map = aod_read_map,
.map_size = ARRAY_SIZE(aod_read_map),
.default_result = 0,
};
return get_query_result_from_register(test, &aod_query);
}
int tk4c_query_get_refresh_rate(struct gs_panel_test *test)
{
static const u8 rr_120[] = { 0x00, 0x00 };
static const u8 rr_60[] = { 0x08, 0x00 };
static const struct array_to_value rr_read_map[] = {
{ .array = rr_120, .value = 120 },
{ .array = rr_60, .value = 60 },
};
static const struct gs_panel_register_query refresh_rate_query = {
.reg = &tk4c_registers[4],
.map = rr_read_map,
.map_size = ARRAY_SIZE(rr_read_map),
.default_result = 0,
};
if (tk4c_query_is_aod_on(test) == 1)
return 30;
return get_query_result_from_register(test, &refresh_rate_query);
}
int tk4c_query_is_irc_on(struct gs_panel_test *test)
{
static const u8 irc_on[] = { 0x25 };
static const u8 ifc_off[] = { 0x05 };
static const struct array_to_value irc_read_map[] = {
{ .array = irc_on, .value = 1 },
{ .array = ifc_off, .value = 0 },
};
static const struct gs_panel_register_query irc_query = {
.reg = &tk4c_registers[2],
.map = irc_read_map,
.map_size = ARRAY_SIZE(irc_read_map),
.default_result = -1,
};
return get_query_result_from_register(test, &irc_query);
}
const struct gs_panel_query_funcs tk4c_gs_query_func = {
.get_refresh_rate = tk4c_query_get_refresh_rate,
.get_irc_on = tk4c_query_is_irc_on,
.get_aod_on = tk4c_query_is_aod_on,
};
/* Custom query functions */
static int tk4c_query_fgz_on(struct gs_panel_test *test)
{
static u8 fgz_off_value[] = { 0xB0, 0x2C, 0x6A, 0x80, 0x00, 0x00, 0x00, 0x00 };
static u8 fgz_on_value_EVT[] = { 0xB0, 0x2C, 0x6A, 0x80, 0x00, 0x00, 0xF5, 0xC4 };
static u8 fgz_on_value_DVT[] = { 0xB0, 0x2C, 0x6A, 0x80, 0x00, 0x00, 0xE4, 0xB6 };
static u8 fgz_on_value_PVT[] = { 0xB0, 0x2C, 0x6A, 0x80, 0x00, 0x00, 0xE4, 0xB6 };
static const struct array_to_value fgz_read_map[] = {
{ .array = fgz_off_value, .value = 0 },
{ .array = fgz_on_value_EVT, .value = 1, .rev = PANEL_REV_LT(PANEL_REV_DVT1) },
{ .array = fgz_on_value_DVT,
.value = 1,
.rev = (PANEL_REV_DVT1 | PANEL_REV_DVT1_1) },
{ .array = fgz_on_value_PVT, .value = 1, .rev = PANEL_REV_GE(PANEL_REV_PVT) },
};
static const struct gs_panel_register_query fgz_query = {
.reg = &tk4c_registers[3],
.map = fgz_read_map,
.map_size = ARRAY_SIZE(fgz_read_map),
.default_result = -1,
};
return get_query_result_from_register(test, &fgz_query);
}
static int tk4c_fgz_show(struct seq_file *m, void *data)
{
struct gs_panel_test *test = m->private;
if (!test)
return -EFAULT;
seq_printf(m, "%d\n", tk4c_query_fgz_on(test));
return 0;
}
DEFINE_SHOW_ATTRIBUTE(tk4c_fgz);
int tk4c_add_custom_query_nodes(struct gs_panel_test *test, struct dentry *test_root)
{
struct dentry *query_root;
query_root = debugfs_lookup("query", test_root);
if (!query_root)
return -EFAULT;
debugfs_create_file("fgz_on", 0600, query_root, test, &tk4c_fgz_fops);
/* TODO: add OPR */
return 0;
}
/**
* struct tk4c_panel_test - panel specific test runtime info
*
* Only maintains tk4c panel test specific runtime info, any fixed details about panel
* should most likely go into struct gs_panel_test_desc
*/
struct tk4c_panel_test {
/** @base: base panel struct */
struct gs_panel_test base;
/* add panel specific test data here */
};
#define to_spanel_test(test) container_of(test, struct tk4c_panel_test, base)
static void tk4c_test_debugfs_init(struct gs_panel_test *test, struct dentry *test_root)
{
tk4c_add_custom_query_nodes(test, test_root);
}
static const struct gs_panel_test_funcs tk4c_test_func = {
.debugfs_init = tk4c_test_debugfs_init,
};
static const struct gs_panel_test_desc google_tk4c_test = {
.test_funcs = &tk4c_test_func,
.regs_desc = &tk4c_reg_desc,
.query_desc = &tk4c_gs_query_func,
};
static int tk4c_panel_test_probe(struct platform_device *pdev)
{
struct tk4c_panel_test *stest;
stest = devm_kzalloc(&pdev->dev, sizeof(*stest), GFP_KERNEL);
if (!stest)
return -ENOMEM;
return gs_panel_test_common_init(pdev, &stest->base);
}
static const struct of_device_id gs_panel_test_of_match[] = {
{ .compatible = "google,gs-tk4c-test", .data = &google_tk4c_test },
{ }
};
MODULE_DEVICE_TABLE(of, gs_panel_test_of_match);
static struct platform_driver gs_panel_test_driver = {
.probe = tk4c_panel_test_probe,
.remove = gs_panel_test_common_remove,
.driver = {
.name = "gs-tk4c-test",
.of_match_table = gs_panel_test_of_match,
},
};
module_platform_driver(gs_panel_test_driver);
MODULE_AUTHOR("Safayat Ullah <safayat@google.com>");
MODULE_DESCRIPTION("MIPI-DSI based Google tk4c panel test driver");
MODULE_LICENSE("Dual MIT/GPL");