kernel_samsung_a53x/drivers/gpu/drm/samsung/panel/copr.c
2024-06-15 16:02:09 -03:00

1729 lines
52 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) Samsung Electronics Co., 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.
*/
#include <linux/of_gpio.h>
#include "panel.h"
#include "panel_drv.h"
#include "panel_debug.h"
#include "copr.h"
static struct copr_reg_info copr_reg_v0_list[] = {
{ .name = "copr_gamma=", .offset = offsetof(struct copr_reg_v0, copr_gamma) },
{ .name = "copr_en=", .offset = offsetof(struct copr_reg_v0, copr_en) },
{ .name = "copr_er=", .offset = offsetof(struct copr_reg_v0, copr_er) },
{ .name = "copr_eg=", .offset = offsetof(struct copr_reg_v0, copr_eg) },
{ .name = "copr_eb=", .offset = offsetof(struct copr_reg_v0, copr_eb) },
{ .name = "copr_roi_on=", .offset = offsetof(struct copr_reg_v0, roi_on) },
{ .name = "copr_roi_x_s=", .offset = offsetof(struct copr_reg_v0, roi_xs) },
{ .name = "copr_roi_y_s=", .offset = offsetof(struct copr_reg_v0, roi_ys) },
{ .name = "copr_roi_x_e=", .offset = offsetof(struct copr_reg_v0, roi_xe) },
{ .name = "copr_roi_y_e=", .offset = offsetof(struct copr_reg_v0, roi_ye) },
};
static struct copr_reg_info copr_reg_v1_list[] = {
{ .name = "copr_cnt_re=", .offset = offsetof(struct copr_reg_v1, cnt_re) },
{ .name = "copr_gamma=", .offset = offsetof(struct copr_reg_v1, copr_gamma) },
{ .name = "copr_en=", .offset = offsetof(struct copr_reg_v1, copr_en) },
{ .name = "copr_er=", .offset = offsetof(struct copr_reg_v1, copr_er) },
{ .name = "copr_eg=", .offset = offsetof(struct copr_reg_v1, copr_eg) },
{ .name = "copr_eb=", .offset = offsetof(struct copr_reg_v1, copr_eb) },
{ .name = "copr_max_cnt=", .offset = offsetof(struct copr_reg_v1, max_cnt) },
{ .name = "copr_roi_on=", .offset = offsetof(struct copr_reg_v1, roi_on) },
{ .name = "copr_roi_x_s=", .offset = offsetof(struct copr_reg_v1, roi_xs) },
{ .name = "copr_roi_y_s=", .offset = offsetof(struct copr_reg_v1, roi_ys) },
{ .name = "copr_roi_x_e=", .offset = offsetof(struct copr_reg_v1, roi_xe) },
{ .name = "copr_roi_y_e=", .offset = offsetof(struct copr_reg_v1, roi_ye) },
};
static struct copr_reg_info copr_reg_v2_list[] = {
{ .name = "copr_cnt_re=", .offset = offsetof(struct copr_reg_v2, cnt_re) },
{ .name = "copr_ilc=", .offset = offsetof(struct copr_reg_v2, copr_ilc) },
{ .name = "copr_gamma=", .offset = offsetof(struct copr_reg_v2, copr_gamma) },
{ .name = "copr_en=", .offset = offsetof(struct copr_reg_v2, copr_en) },
{ .name = "copr_er=", .offset = offsetof(struct copr_reg_v2, copr_er) },
{ .name = "copr_eg=", .offset = offsetof(struct copr_reg_v2, copr_eg) },
{ .name = "copr_eb=", .offset = offsetof(struct copr_reg_v2, copr_eb) },
{ .name = "copr_erc=", .offset = offsetof(struct copr_reg_v2, copr_erc) },
{ .name = "copr_egc=", .offset = offsetof(struct copr_reg_v2, copr_egc) },
{ .name = "copr_ebc=", .offset = offsetof(struct copr_reg_v2, copr_ebc) },
{ .name = "copr_max_cnt=", .offset = offsetof(struct copr_reg_v2, max_cnt) },
{ .name = "copr_roi_on=", .offset = offsetof(struct copr_reg_v2, roi_on) },
{ .name = "copr_roi_x_s=", .offset = offsetof(struct copr_reg_v2, roi_xs) },
{ .name = "copr_roi_y_s=", .offset = offsetof(struct copr_reg_v2, roi_ys) },
{ .name = "copr_roi_x_e=", .offset = offsetof(struct copr_reg_v2, roi_xe) },
{ .name = "copr_roi_y_e=", .offset = offsetof(struct copr_reg_v2, roi_ye) },
};
static struct copr_reg_info copr_reg_v3_list[] = {
{ .name = "copr_mask=", .offset = offsetof(struct copr_reg_v3, copr_mask) },
{ .name = "copr_cnt_re=", .offset = offsetof(struct copr_reg_v3, cnt_re) },
{ .name = "copr_ilc=", .offset = offsetof(struct copr_reg_v3, copr_ilc) },
{ .name = "copr_gamma=", .offset = offsetof(struct copr_reg_v3, copr_gamma) },
{ .name = "copr_en=", .offset = offsetof(struct copr_reg_v3, copr_en) },
{ .name = "copr_er=", .offset = offsetof(struct copr_reg_v3, copr_er) },
{ .name = "copr_eg=", .offset = offsetof(struct copr_reg_v3, copr_eg) },
{ .name = "copr_eb=", .offset = offsetof(struct copr_reg_v3, copr_eb) },
{ .name = "copr_erc=", .offset = offsetof(struct copr_reg_v3, copr_erc) },
{ .name = "copr_egc=", .offset = offsetof(struct copr_reg_v3, copr_egc) },
{ .name = "copr_ebc=", .offset = offsetof(struct copr_reg_v3, copr_ebc) },
{ .name = "copr_max_cnt=", .offset = offsetof(struct copr_reg_v3, max_cnt) },
{ .name = "copr_roi_ctrl=", .offset = offsetof(struct copr_reg_v3, roi_on) },
/* ROI1 */
{ .name = "copr_roi1_x_s=", .offset = offsetof(struct copr_reg_v3, roi[0].roi_xs) },
{ .name = "copr_roi1_y_s=", .offset = offsetof(struct copr_reg_v3, roi[0].roi_ys) },
{ .name = "copr_roi1_x_e=", .offset = offsetof(struct copr_reg_v3, roi[0].roi_xe) },
{ .name = "copr_roi1_y_e=", .offset = offsetof(struct copr_reg_v3, roi[0].roi_ye) },
/* ROI2 */
{ .name = "copr_roi2_x_s=", .offset = offsetof(struct copr_reg_v3, roi[1].roi_xs) },
{ .name = "copr_roi2_y_s=", .offset = offsetof(struct copr_reg_v3, roi[1].roi_ys) },
{ .name = "copr_roi2_x_e=", .offset = offsetof(struct copr_reg_v3, roi[1].roi_xe) },
{ .name = "copr_roi2_y_e=", .offset = offsetof(struct copr_reg_v3, roi[1].roi_ye) },
/* ROI3 */
{ .name = "copr_roi3_x_s=", .offset = offsetof(struct copr_reg_v3, roi[2].roi_xs) },
{ .name = "copr_roi3_y_s=", .offset = offsetof(struct copr_reg_v3, roi[2].roi_ys) },
{ .name = "copr_roi3_x_e=", .offset = offsetof(struct copr_reg_v3, roi[2].roi_xe) },
{ .name = "copr_roi3_y_e=", .offset = offsetof(struct copr_reg_v3, roi[2].roi_ye) },
/* ROI4 */
{ .name = "copr_roi4_x_s=", .offset = offsetof(struct copr_reg_v3, roi[3].roi_xs) },
{ .name = "copr_roi4_y_s=", .offset = offsetof(struct copr_reg_v3, roi[3].roi_ys) },
{ .name = "copr_roi4_x_e=", .offset = offsetof(struct copr_reg_v3, roi[3].roi_xe) },
{ .name = "copr_roi4_y_e=", .offset = offsetof(struct copr_reg_v3, roi[3].roi_ye) },
/* ROI5 */
{ .name = "copr_roi5_x_s=", .offset = offsetof(struct copr_reg_v3, roi[4].roi_xs) },
{ .name = "copr_roi5_y_s=", .offset = offsetof(struct copr_reg_v3, roi[4].roi_ys) },
{ .name = "copr_roi5_x_e=", .offset = offsetof(struct copr_reg_v3, roi[4].roi_xe) },
{ .name = "copr_roi5_y_e=", .offset = offsetof(struct copr_reg_v3, roi[4].roi_ye) },
/* ROI6 */
{ .name = "copr_roi6_x_s=", .offset = offsetof(struct copr_reg_v3, roi[5].roi_xs) },
{ .name = "copr_roi6_y_s=", .offset = offsetof(struct copr_reg_v3, roi[5].roi_ys) },
{ .name = "copr_roi6_x_e=", .offset = offsetof(struct copr_reg_v3, roi[5].roi_xe) },
{ .name = "copr_roi6_y_e=", .offset = offsetof(struct copr_reg_v3, roi[5].roi_ye) },
};
static struct copr_reg_info copr_reg_v5_list[] = {
{ .name = "copr_mask=", .offset = offsetof(struct copr_reg_v5, copr_mask) },
{ .name = "copr_cnt_re=", .offset = offsetof(struct copr_reg_v5, cnt_re) },
{ .name = "copr_ilc=", .offset = offsetof(struct copr_reg_v5, copr_ilc) },
{ .name = "copr_gamma=", .offset = offsetof(struct copr_reg_v5, copr_gamma) },
{ .name = "copr_en=", .offset = offsetof(struct copr_reg_v5, copr_en) },
{ .name = "copr_er=", .offset = offsetof(struct copr_reg_v5, copr_er) },
{ .name = "copr_eg=", .offset = offsetof(struct copr_reg_v5, copr_eg) },
{ .name = "copr_eb=", .offset = offsetof(struct copr_reg_v5, copr_eb) },
{ .name = "copr_erc=", .offset = offsetof(struct copr_reg_v5, copr_erc) },
{ .name = "copr_egc=", .offset = offsetof(struct copr_reg_v5, copr_egc) },
{ .name = "copr_ebc=", .offset = offsetof(struct copr_reg_v5, copr_ebc) },
{ .name = "copr_max_cnt=", .offset = offsetof(struct copr_reg_v5, max_cnt) },
{ .name = "copr_roi_ctrl=", .offset = offsetof(struct copr_reg_v5, roi_on) },
/* ROI1 */
{ .name = "copr_roi1_x_s=", .offset = offsetof(struct copr_reg_v5, roi[0].roi_xs) },
{ .name = "copr_roi1_y_s=", .offset = offsetof(struct copr_reg_v5, roi[0].roi_ys) },
{ .name = "copr_roi1_x_e=", .offset = offsetof(struct copr_reg_v5, roi[0].roi_xe) },
{ .name = "copr_roi1_y_e=", .offset = offsetof(struct copr_reg_v5, roi[0].roi_ye) },
/* ROI2 */
{ .name = "copr_roi2_x_s=", .offset = offsetof(struct copr_reg_v5, roi[1].roi_xs) },
{ .name = "copr_roi2_y_s=", .offset = offsetof(struct copr_reg_v5, roi[1].roi_ys) },
{ .name = "copr_roi2_x_e=", .offset = offsetof(struct copr_reg_v5, roi[1].roi_xe) },
{ .name = "copr_roi2_y_e=", .offset = offsetof(struct copr_reg_v5, roi[1].roi_ye) },
/* ROI3 */
{ .name = "copr_roi3_x_s=", .offset = offsetof(struct copr_reg_v5, roi[2].roi_xs) },
{ .name = "copr_roi3_y_s=", .offset = offsetof(struct copr_reg_v5, roi[2].roi_ys) },
{ .name = "copr_roi3_x_e=", .offset = offsetof(struct copr_reg_v5, roi[2].roi_xe) },
{ .name = "copr_roi3_y_e=", .offset = offsetof(struct copr_reg_v5, roi[2].roi_ye) },
/* ROI4 */
{ .name = "copr_roi4_x_s=", .offset = offsetof(struct copr_reg_v5, roi[3].roi_xs) },
{ .name = "copr_roi4_y_s=", .offset = offsetof(struct copr_reg_v5, roi[3].roi_ys) },
{ .name = "copr_roi4_x_e=", .offset = offsetof(struct copr_reg_v5, roi[3].roi_xe) },
{ .name = "copr_roi4_y_e=", .offset = offsetof(struct copr_reg_v5, roi[3].roi_ye) },
/* ROI5 */
{ .name = "copr_roi5_x_s=", .offset = offsetof(struct copr_reg_v5, roi[4].roi_xs) },
{ .name = "copr_roi5_y_s=", .offset = offsetof(struct copr_reg_v5, roi[4].roi_ys) },
{ .name = "copr_roi5_x_e=", .offset = offsetof(struct copr_reg_v5, roi[4].roi_xe) },
{ .name = "copr_roi5_y_e=", .offset = offsetof(struct copr_reg_v5, roi[4].roi_ye) },
};
static struct copr_reg_info copr_reg_v6_list[] = {
{ .name = "copr_mask=", .offset = offsetof(struct copr_reg_v6, copr_mask) },
{ .name = "copr_pwr=", .offset = offsetof(struct copr_reg_v6, copr_pwr) },
{ .name = "copr_en=", .offset = offsetof(struct copr_reg_v6, copr_en) },
{ .name = "copr_roi_ctrl=", .offset = offsetof(struct copr_reg_v6, roi_on) },
{ .name = "copr_gamma=", .offset = offsetof(struct copr_reg_v6, copr_gamma) },
{ .name = "copr_frame_count=", .offset = offsetof(struct copr_reg_v6, copr_frame_count) },
/* ROI1 */
{ .name = "copr_roi1_er=", .offset = offsetof(struct copr_reg_v6, roi[0].roi_er) },
{ .name = "copr_roi1_eg=", .offset = offsetof(struct copr_reg_v6, roi[0].roi_eg) },
{ .name = "copr_roi1_eb=", .offset = offsetof(struct copr_reg_v6, roi[0].roi_eb) },
/* ROI2 */
{ .name = "copr_roi2_er=", .offset = offsetof(struct copr_reg_v6, roi[1].roi_er) },
{ .name = "copr_roi2_eg=", .offset = offsetof(struct copr_reg_v6, roi[1].roi_eg) },
{ .name = "copr_roi2_eb=", .offset = offsetof(struct copr_reg_v6, roi[1].roi_eb) },
/* ROI3 */
{ .name = "copr_roi3_er=", .offset = offsetof(struct copr_reg_v6, roi[2].roi_er) },
{ .name = "copr_roi3_eg=", .offset = offsetof(struct copr_reg_v6, roi[2].roi_eg) },
{ .name = "copr_roi3_eb=", .offset = offsetof(struct copr_reg_v6, roi[2].roi_eb) },
/* ROI4 */
{ .name = "copr_roi4_er=", .offset = offsetof(struct copr_reg_v6, roi[3].roi_er) },
{ .name = "copr_roi4_eg=", .offset = offsetof(struct copr_reg_v6, roi[3].roi_eg) },
{ .name = "copr_roi4_eb=", .offset = offsetof(struct copr_reg_v6, roi[3].roi_eb) },
/* ROI5 */
{ .name = "copr_roi5_er=", .offset = offsetof(struct copr_reg_v6, roi[4].roi_er) },
{ .name = "copr_roi5_eg=", .offset = offsetof(struct copr_reg_v6, roi[4].roi_eg) },
{ .name = "copr_roi5_eb=", .offset = offsetof(struct copr_reg_v6, roi[4].roi_eb) },
/* ROI1 */
{ .name = "copr_roi1_x_s=", .offset = offsetof(struct copr_reg_v6, roi[0].roi_xs) },
{ .name = "copr_roi1_y_s=", .offset = offsetof(struct copr_reg_v6, roi[0].roi_ys) },
{ .name = "copr_roi1_x_e=", .offset = offsetof(struct copr_reg_v6, roi[0].roi_xe) },
{ .name = "copr_roi1_y_e=", .offset = offsetof(struct copr_reg_v6, roi[0].roi_ye) },
/* ROI2 */
{ .name = "copr_roi2_x_s=", .offset = offsetof(struct copr_reg_v6, roi[1].roi_xs) },
{ .name = "copr_roi2_y_s=", .offset = offsetof(struct copr_reg_v6, roi[1].roi_ys) },
{ .name = "copr_roi2_x_e=", .offset = offsetof(struct copr_reg_v6, roi[1].roi_xe) },
{ .name = "copr_roi2_y_e=", .offset = offsetof(struct copr_reg_v6, roi[1].roi_ye) },
/* ROI3 */
{ .name = "copr_roi3_x_s=", .offset = offsetof(struct copr_reg_v6, roi[2].roi_xs) },
{ .name = "copr_roi3_y_s=", .offset = offsetof(struct copr_reg_v6, roi[2].roi_ys) },
{ .name = "copr_roi3_x_e=", .offset = offsetof(struct copr_reg_v6, roi[2].roi_xe) },
{ .name = "copr_roi3_y_e=", .offset = offsetof(struct copr_reg_v6, roi[2].roi_ye) },
/* ROI4 */
{ .name = "copr_roi4_x_s=", .offset = offsetof(struct copr_reg_v6, roi[3].roi_xs) },
{ .name = "copr_roi4_y_s=", .offset = offsetof(struct copr_reg_v6, roi[3].roi_ys) },
{ .name = "copr_roi4_x_e=", .offset = offsetof(struct copr_reg_v6, roi[3].roi_xe) },
{ .name = "copr_roi4_y_e=", .offset = offsetof(struct copr_reg_v6, roi[3].roi_ye) },
/* ROI5 */
{ .name = "copr_roi5_x_s=", .offset = offsetof(struct copr_reg_v6, roi[4].roi_xs) },
{ .name = "copr_roi5_y_s=", .offset = offsetof(struct copr_reg_v6, roi[4].roi_ys) },
{ .name = "copr_roi5_x_e=", .offset = offsetof(struct copr_reg_v6, roi[4].roi_xe) },
{ .name = "copr_roi5_y_e=", .offset = offsetof(struct copr_reg_v6, roi[4].roi_ye) },
};
static int get_copr_ver(struct copr_info *copr)
{
return copr->props.version;
}
static void SET_COPR_REG_GAMMA(struct copr_info *copr, unsigned int copr_gamma)
{
u32 version = get_copr_ver(copr);
if (version == COPR_VER_0)
copr->props.reg.v0.copr_gamma = copr_gamma;
else if (version == COPR_VER_1)
copr->props.reg.v1.copr_gamma = copr_gamma;
else if (version == COPR_VER_2)
copr->props.reg.v2.copr_gamma = copr_gamma;
else if (version == COPR_VER_3)
copr->props.reg.v3.copr_gamma = copr_gamma;
else if (version == COPR_VER_5)
copr->props.reg.v5.copr_gamma = copr_gamma;
else if (version == COPR_VER_6)
copr->props.reg.v6.copr_gamma = copr_gamma;
else
panel_warn("unsupprted in ver%d\n", version);
}
static void SET_COPR_REG_E(struct copr_info *copr, int r, int g, int b)
{
u32 version = get_copr_ver(copr);
if (version == COPR_VER_0) {
copr->props.reg.v0.copr_er = r;
copr->props.reg.v0.copr_eg = g;
copr->props.reg.v0.copr_eb = b;
} else if (version == COPR_VER_1) {
copr->props.reg.v1.copr_er = r;
copr->props.reg.v1.copr_eg = g;
copr->props.reg.v1.copr_eb = b;
} else if (version == COPR_VER_2) {
copr->props.reg.v2.copr_er = r;
copr->props.reg.v2.copr_eg = g;
copr->props.reg.v2.copr_eb = b;
} else if (version == COPR_VER_3) {
copr->props.reg.v3.copr_er = r;
copr->props.reg.v3.copr_eg = g;
copr->props.reg.v3.copr_eb = b;
} else if (version == COPR_VER_5) {
copr->props.reg.v5.copr_er = r;
copr->props.reg.v5.copr_eg = g;
copr->props.reg.v5.copr_eb = b;
} else if (version == COPR_VER_6) {
panel_warn("unsupprted in ver%d\n", version);
} else {
panel_warn("unsupprted in ver%d\n", version);
}
}
static void SET_COPR_REG_EC(struct copr_info *copr, int r, int g, int b)
{
u32 version = get_copr_ver(copr);
if (version == COPR_VER_0) {
panel_warn("unsupprted in ver%d\n", version);
} else if (version == COPR_VER_1) {
panel_warn("unsupprted in ver%d\n", version);
} else if (version == COPR_VER_2) {
copr->props.reg.v2.copr_erc = r;
copr->props.reg.v2.copr_egc = g;
copr->props.reg.v2.copr_ebc = b;
} else if (version == COPR_VER_3) {
copr->props.reg.v3.copr_erc = r;
copr->props.reg.v3.copr_egc = g;
copr->props.reg.v3.copr_ebc = b;
} else if (version == COPR_VER_5) {
copr->props.reg.v5.copr_erc = r;
copr->props.reg.v5.copr_egc = g;
copr->props.reg.v5.copr_ebc = b;
} else if (version == COPR_VER_6) {
panel_warn("unsupprted in ver%d\n", version);
} else {
panel_warn("unsupprted in ver%d\n", version);
}
}
#if 0
static void SET_COPR_REG_CNT_RE(struct copr_info *copr, int cnt_re)
{
u32 version = get_copr_ver(copr);
if (version == COPR_VER_0)
panel_warn("unsupprted in ver%d\n", version);
else if (version == COPR_VER_1)
copr->props.reg.v1.cnt_re = cnt_re;
else if (version == COPR_VER_2)
copr->props.reg.v2.cnt_re = cnt_re;
else if (version == COPR_VER_3)
copr->props.reg.v3.cnt_re = cnt_re;
else if (version == COPR_VER_5)
copr->props.reg.v5.cnt_re = cnt_re;
else if (version == COPR_VER_6)
panel_warn("unsupprted in ver%d\n", version);
else
panel_warn("unsupprted in ver%d\n", version);
}
#endif
static void SET_COPR_REG_ROI(struct copr_info *copr, struct copr_roi *roi, int nr_roi)
{
u32 version = get_copr_ver(copr);
struct copr_properties *props = &copr->props;
int i;
if (version == COPR_VER_2) {
if (roi == NULL) {
props->reg.v2.roi_xs = 0;
props->reg.v2.roi_ys = 0;
props->reg.v2.roi_xe = 0;
props->reg.v2.roi_ye = 0;
props->reg.v2.roi_on = 0;
} else {
props->reg.v2.roi_xs = roi[0].roi_xs;
props->reg.v2.roi_ys = roi[0].roi_ys;
props->reg.v2.roi_xe = roi[0].roi_xe;
props->reg.v2.roi_ye = roi[0].roi_ye;
props->reg.v2.roi_on = true;
}
} else if (version == COPR_VER_3) {
if (roi == NULL) {
props->reg.v3.roi_on = 0;
memset(props->reg.v3.roi, 0, sizeof(props->reg.v3.roi));
} else {
props->reg.v3.roi_on = 0;
for (i = 0; i < min_t(int, ARRAY_SIZE(props->reg.v3.roi), nr_roi); i++) {
props->reg.v3.roi[i].roi_xs = roi[i].roi_xs;
props->reg.v3.roi[i].roi_ys = roi[i].roi_ys;
props->reg.v3.roi[i].roi_xe = roi[i].roi_xe;
props->reg.v3.roi[i].roi_ye = roi[i].roi_ye;
props->reg.v3.roi_on |= 0x1 << i;
}
}
} else if (version == COPR_VER_5) {
if (roi == NULL) {
props->reg.v5.roi_on = 0;
memset(props->reg.v5.roi, 0, sizeof(props->reg.v5.roi));
} else {
props->reg.v5.roi_on = 0;
for (i = 0; i < min_t(int, ARRAY_SIZE(props->reg.v5.roi), nr_roi); i++) {
props->reg.v5.roi[i].roi_xs = roi[i].roi_xs;
props->reg.v5.roi[i].roi_ys = roi[i].roi_ys;
props->reg.v5.roi[i].roi_xe = roi[i].roi_xe;
props->reg.v5.roi[i].roi_ye = roi[i].roi_ye;
props->reg.v5.roi_on |= 0x1 << i;
}
}
} else if (version == COPR_VER_6) {
if (roi == NULL) {
props->reg.v6.roi_on = 0;
memset(props->reg.v6.roi, 0, sizeof(props->reg.v6.roi));
} else {
props->reg.v6.roi_on = 0;
for (i = 0; i < min_t(int, ARRAY_SIZE(props->reg.v6.roi), nr_roi); i++) {
props->reg.v6.roi[i].roi_er = roi[i].roi_er;
props->reg.v6.roi[i].roi_eg = roi[i].roi_eg;
props->reg.v6.roi[i].roi_eb = roi[i].roi_eb;
props->reg.v6.roi[i].roi_xs = roi[i].roi_xs;
props->reg.v6.roi[i].roi_ys = roi[i].roi_ys;
props->reg.v6.roi[i].roi_xe = roi[i].roi_xe;
props->reg.v6.roi[i].roi_ye = roi[i].roi_ye;
props->reg.v6.roi_on |= 0x1 << i;
}
}
}
}
int get_copr_reg_copr_en(struct copr_info *copr)
{
u32 version = get_copr_ver(copr);
int copr_en = COPR_REG_OFF;
if (version == COPR_VER_0)
copr_en = copr->props.reg.v0.copr_en;
else if (version == COPR_VER_1)
copr_en = copr->props.reg.v1.copr_en;
else if (version == COPR_VER_2)
copr_en = copr->props.reg.v2.copr_en;
else if (version == COPR_VER_3)
copr_en = copr->props.reg.v3.copr_en;
else if (version == COPR_VER_5)
copr_en = copr->props.reg.v5.copr_en;
else if (version == COPR_VER_6)
copr_en = copr->props.reg.v6.copr_en;
else
panel_warn("unsupprted in ver%d\n", version);
return copr_en;
}
int get_copr_reg_size(int version)
{
if (version == COPR_VER_0)
return ARRAY_SIZE(copr_reg_v0_list);
else if (version == COPR_VER_1)
return ARRAY_SIZE(copr_reg_v1_list);
else if (version == COPR_VER_2)
return ARRAY_SIZE(copr_reg_v2_list);
else if (version == COPR_VER_3)
return ARRAY_SIZE(copr_reg_v3_list);
else if (version == COPR_VER_5)
return ARRAY_SIZE(copr_reg_v5_list);
else if (version == COPR_VER_6)
return ARRAY_SIZE(copr_reg_v6_list);
else
return 0;
}
EXPORT_SYMBOL(get_copr_reg_size);
int get_copr_reg_packed_size(int version)
{
if (version == COPR_VER_5)
return COPR_V5_CTRL_REG_SIZE;
else if (version == COPR_VER_6)
return COPR_V6_CTRL_REG_SIZE;
else
return 0;
}
EXPORT_SYMBOL(get_copr_reg_packed_size);
const char *get_copr_reg_name(int version, int index)
{
if (version == COPR_VER_0)
return copr_reg_v0_list[index].name;
else if (version == COPR_VER_1)
return copr_reg_v1_list[index].name;
else if (version == COPR_VER_2)
return copr_reg_v2_list[index].name;
else if (version == COPR_VER_3)
return copr_reg_v3_list[index].name;
else if (version == COPR_VER_5)
return copr_reg_v5_list[index].name;
else if (version == COPR_VER_6)
return copr_reg_v6_list[index].name;
else
return NULL;
}
EXPORT_SYMBOL(get_copr_reg_name);
int get_copr_reg_offset(int version, int index)
{
if (version == COPR_VER_0)
return copr_reg_v0_list[index].offset;
else if (version == COPR_VER_1)
return copr_reg_v1_list[index].offset;
else if (version == COPR_VER_2)
return copr_reg_v2_list[index].offset;
else if (version == COPR_VER_3)
return copr_reg_v3_list[index].offset;
else if (version == COPR_VER_5)
return copr_reg_v5_list[index].offset;
else if (version == COPR_VER_6)
return copr_reg_v6_list[index].offset;
else
return -EINVAL;
}
EXPORT_SYMBOL(get_copr_reg_offset);
u32 *get_copr_reg_ptr(struct copr_reg *reg, int version, int index)
{
int offset = get_copr_reg_offset(version, index);
if (offset < 0)
return NULL;
if (version == COPR_VER_0)
return (u32 *)((void *)&reg->v0 + offset);
else if (version == COPR_VER_1)
return (u32 *)((void *)&reg->v1 + offset);
else if (version == COPR_VER_2)
return (u32 *)((void *)&reg->v2 + offset);
else if (version == COPR_VER_3)
return (u32 *)((void *)&reg->v3 + offset);
else if (version == COPR_VER_5)
return (u32 *)((void *)&reg->v5 + offset);
else if (version == COPR_VER_6)
return (u32 *)((void *)&reg->v6 + offset);
else
return NULL;
}
EXPORT_SYMBOL(get_copr_reg_ptr);
int find_copr_reg_by_name(int version, char *s)
{
int i;
const char *name;
if (s == NULL)
return -EINVAL;
for (i = 0; i < get_copr_reg_size(version); i++) {
name = get_copr_reg_name(version, i);
if (name == NULL)
continue;
if (!strncmp(name, s, strlen(name)))
return i;
}
return -EINVAL;
}
EXPORT_SYMBOL(find_copr_reg_by_name);
int copr_reg_to_byte_array(struct copr_reg *reg, int version, unsigned char *byte_array)
{
int i, offset = 0;
if (version == COPR_VER_5) {
struct copr_reg_v5 *r = &reg->v5;
byte_array[offset++] = (r->copr_mask << 5) | (r->cnt_re << 4) |
(r->copr_ilc << 3) | (r->copr_gamma << 1) | r->copr_en;
byte_array[offset++] = ((r->copr_er >> 8) & 0x3) << 4 |
((r->copr_eg >> 8) & 0x3) << 2 | ((r->copr_eb >> 8) & 0x3);
byte_array[offset++] = ((r->copr_erc >> 8) & 0x3) << 4 |
((r->copr_egc >> 8) & 0x3) << 2 | ((r->copr_ebc >> 8) & 0x3);
byte_array[offset++] = r->copr_er;
byte_array[offset++] = r->copr_eg;
byte_array[offset++] = r->copr_eb;
byte_array[offset++] = r->copr_erc;
byte_array[offset++] = r->copr_egc;
byte_array[offset++] = r->copr_ebc;
byte_array[offset++] = (r->max_cnt >> 8) & 0xFF;
byte_array[offset++] = r->max_cnt & 0xFF;
byte_array[offset++] = r->roi_on;
for (i = 0; i < 5; i++) {
byte_array[offset++] = (r->roi[i].roi_xs >> 8) & 0x7;
byte_array[offset++] = r->roi[i].roi_xs & 0xFF;
byte_array[offset++] = (r->roi[i].roi_ys >> 8) & 0xF;
byte_array[offset++] = r->roi[i].roi_ys & 0xFF;
byte_array[offset++] = (r->roi[i].roi_xe >> 8) & 0x7;
byte_array[offset++] = r->roi[i].roi_xe & 0xFF;
byte_array[offset++] = (r->roi[i].roi_ye >> 8) & 0xF;
byte_array[offset++] = r->roi[i].roi_ye & 0xFF;
}
} else if (version == COPR_VER_6) {
struct copr_reg_v6 *r = &reg->v6;
byte_array[offset++] = ((r->copr_mask & 0x1) << 4) |
((r->copr_pwr & 0x1) << 1) |
(r->copr_en & 0x1);
byte_array[offset++] = (r->roi_on & 0x1F);
byte_array[offset++] = (r->copr_gamma & 0x1F);
byte_array[offset++] = (r->copr_frame_count >> 8) & 0x3;
byte_array[offset++] = (r->copr_frame_count & 0xFF);
/* COPR_ROI_ER/G/B */
for (i = 0; i < 5; i++) {
byte_array[offset++] = (r->roi[i].roi_er >> 8) & 0x3;
byte_array[offset++] = r->roi[i].roi_er & 0xFF;
byte_array[offset++] = (r->roi[i].roi_eg >> 8) & 0x3;
byte_array[offset++] = r->roi[i].roi_eg & 0xFF;
byte_array[offset++] = (r->roi[i].roi_eb >> 8) & 0x3;
byte_array[offset++] = r->roi[i].roi_eb & 0xFF;
}
/* COPR_ROI_XS/YS/XE/YE */
for (i = 0; i < 5; i++) {
byte_array[offset++] = (r->roi[i].roi_xs >> 8) & 0x7;
byte_array[offset++] = r->roi[i].roi_xs & 0xFF;
byte_array[offset++] = (r->roi[i].roi_ys >> 8) & 0xF;
byte_array[offset++] = r->roi[i].roi_ys & 0xFF;
byte_array[offset++] = (r->roi[i].roi_xe >> 8) & 0x7;
byte_array[offset++] = r->roi[i].roi_xe & 0xFF;
byte_array[offset++] = (r->roi[i].roi_ye >> 8) & 0xF;
byte_array[offset++] = r->roi[i].roi_ye & 0xFF;
}
}
return 0;
}
EXPORT_SYMBOL(copr_reg_to_byte_array);
ssize_t copr_reg_show(struct copr_info *copr, char *buf)
{
int i, len = 0, size;
const char *name;
u32 *ptr;
u32 version = get_copr_ver(copr);
size = get_copr_reg_size(version);
for (i = 0; i < size; i++) {
name = get_copr_reg_name(version, i);
ptr = get_copr_reg_ptr(&copr->props.reg, version, i);
if (name != NULL && ptr != NULL)
len += snprintf(buf + len, PAGE_SIZE - len, "%s%d\n", name, *ptr);
}
return len;
}
int copr_reg_store(struct copr_info *copr, int index, u32 value)
{
const char *name;
u32 *ptr;
u32 version = get_copr_ver(copr);
int size = get_copr_reg_size(version);
if (index >= size)
return -EINVAL;
name = get_copr_reg_name(version, index);
ptr = get_copr_reg_ptr(&copr->props.reg, version, index);
if (name != NULL && ptr != NULL)
*ptr = value;
else
return -EINVAL;
return 0;
}
#ifdef CONFIG_PANEL_NOTIFY
static inline void panel_send_coprstate_notify(u32 state)
{
struct panel_copr_event_data data;
data.state = state;
panel_notifier_call_chain(PANEL_EVENT_COPR_STATE_CHANGED, &data);
panel_info("call EVENT_COPR_STATE notifier %d\n", data.state);
}
#endif
static int panel_do_copr_seqtbl_by_index(struct copr_info *copr, int index)
{
struct panel_device *panel = to_panel_device(copr);
struct seqinfo *tbl;
int ret;
if (panel == NULL) {
panel_err("panel is null\n");
return -EINVAL;
}
if (!IS_PANEL_ACTIVE(panel)) {
panel_warn("panel inactive state\n");
return -EINVAL;
}
tbl = panel->copr.seqtbl;
mutex_lock(&panel->op_lock);
if (unlikely(index < 0 || index >= MAX_COPR_SEQ)) {
panel_err("invalid parameter (panel %p, index %d)\n", panel, index);
ret = -EINVAL;
goto do_exit;
}
panel_dbg("%s:start\n", tbl[index].name);
ret = panel_do_seqtbl(panel, &tbl[index]);
if (unlikely(ret < 0)) {
panel_err("failed to excute seqtbl:%s\n", tbl->name);
ret = -EIO;
goto do_exit;
}
do_exit:
mutex_unlock(&panel->op_lock);
panel_dbg("%s:end\n", tbl[index].name);
return 0;
}
static int panel_set_copr(struct copr_info *copr)
{
int ret;
if (unlikely(!copr->props.support))
return -ENODEV;
ret = panel_do_copr_seqtbl_by_index(copr, COPR_SET_SEQ);
if (unlikely(ret < 0)) {
panel_err("failed to do seqtbl\n");
return -EIO;
}
msleep(34);
if (get_copr_reg_copr_en(copr))
copr->props.state = COPR_REG_ON;
else
copr->props.state = COPR_REG_OFF;
return 0;
}
#ifdef CONFIG_SUPPORT_COPR_AVG
static int panel_clear_copr(struct copr_info *copr)
{
int ret = 0;
ret = panel_do_copr_seqtbl_by_index(copr, COPR_CLR_CNT_ON_SEQ);
if (unlikely(ret < 0))
panel_err("failed to do seqtbl\n");
msleep(34);
ret = panel_do_copr_seqtbl_by_index(copr, COPR_CLR_CNT_OFF_SEQ);
if (unlikely(ret < 0))
panel_err("failed to do seqtbl\n");
msleep(34);
panel_dbg("copr clear seq\n");
return ret;
}
#endif
#ifdef CONFIG_EXYNOS_DECON_LCD_SPI
static int panel_read_copr_spi(struct copr_info *copr)
{
u8 *buf = NULL;
int i, c, index, ret, size;
struct panel_device *panel = to_panel_device(copr);
struct panel_info *panel_data;
struct copr_properties *props = &copr->props;
u32 version = get_copr_ver(copr);
int max_color = (version == COPR_VER_6) ?
MAX_RGBW_COLOR : MAX_COLOR;
if (unlikely(!panel)) {
panel_err("panel is null\n");
return -ENODEV;
}
panel_data = &panel->panel_data;
ret = panel_do_copr_seqtbl_by_index(copr, COPR_SPI_GET_SEQ);
if (unlikely(ret < 0)) {
panel_err("failed to do seqtbl\n");
ret = -EIO;
goto get_copr_error;
}
size = get_resource_size_by_name(panel_data, "copr_spi");
if (size < 0) {
panel_err("failed to get copr size (ret %d)\n", size);
ret = -EINVAL;
goto get_copr_error;
}
buf = kzalloc(size, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto get_copr_error;
}
ret = resource_copy_by_name(panel_data, (u8 *)buf, "copr_spi");
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
ret = -EIO;
goto get_copr_error;
}
if (version == COPR_VER_6) {
for (i = 0; i < 5; i++) {
for (c = 0; c < max_color; c++) {
index = i * (max_color * 2) + c * 2;
if (i == 4 && c == RGBW_WHITE) {
/* COPR_ROI5_W:8bit */
props->copr_roi_r[i][c] = buf[index];
} else {
/* COPR_ROI1~5 RGBW:10bit */
props->copr_roi_r[i][c] = (buf[index] << 8) | buf[index + 1];
}
}
panel_dbg("copr_dsi: copr_roi_r[%d] %d %d %d %d\n",
i, props->copr_roi_r[i][RGBW_RED],
props->copr_roi_r[i][RGBW_GREEN],
props->copr_roi_r[i][RGBW_BLUE],
props->copr_roi_r[i][RGBW_WHITE]);
}
} else if (version == COPR_VER_3 || version == COPR_VER_5) {
props->copr_ready = buf[0] & 0x01;
props->cur_cnt = (buf[1] << 8) | buf[2];
props->cur_copr = (buf[3] << 8) | buf[4];
props->avg_copr = (buf[5] << 8) | buf[6];
props->s_cur_cnt = (buf[7] << 8) | buf[8];
props->s_avg_copr = (buf[9] << 8) | buf[10];
for (i = 0; i < 5; i++) {
for (c = 0; c < max_color; c++) {
index = 11 + i * (max_color * 2) + c * 2;
props->copr_roi_r[i][c] = (buf[index] << 8) | buf[index + 1];
}
panel_dbg("copr_spi: copr_roi_r[%d] %d %d %d\n",
i, props->copr_roi_r[i][RED],
props->copr_roi_r[i][GREEN],
props->copr_roi_r[i][BLUE]);
}
panel_dbg("copr_spi: cur_cnt %d, cur_copr %d, avg_copr %d, s_cur_cnt %d, s_avg_copr %d, copr_ready %d, comp_copr %d\n",
props->cur_cnt, props->cur_copr, props->avg_copr,
props->s_cur_cnt, props->s_avg_copr, props->copr_ready, props->comp_copr);
} else if (version == COPR_VER_2) {
props->copr_ready = ((buf[0] & 0x80) ? 1 : 0);
props->cur_cnt = ((buf[0] & 0x7F) << 9) |
(buf[1] << 1) | ((buf[2] & 0x80) ? 1 : 0);
props->cur_copr = ((buf[2] & 0x7F) << 2) | ((buf[3] & 0xC0) >> 6);
props->avg_copr = ((buf[3] & 0x3F) << 3) | ((buf[4] & 0xE0) >> 5);
props->s_cur_cnt = ((buf[4] & 0x1F) << 11) |
(buf[5] << 3) | ((buf[6] & 0xE0) >> 5);
props->s_avg_copr = ((buf[6] & 0x1F) << 4) | ((buf[7] & 0xF0) >> 4);
props->comp_copr = ((buf[7] & 0x0F) << 5) | ((buf[8] & 0xF8) >> 3);
panel_dbg("copr_spi: cur_cnt %d, cur_copr %d, avg_copr %d, s_cur_cnt %d, s_avg_copr %d, copr_ready %d, comp_copr %d\n",
props->cur_cnt, props->cur_copr, props->avg_copr,
props->s_cur_cnt, props->s_avg_copr, props->copr_ready, props->comp_copr);
} else if (version == COPR_VER_1) {
props->copr_ready = ((buf[0] & 0x80) ? 1 : 0);
props->cur_cnt = ((buf[0] & 0x7F) << 9) |
(buf[1] << 1) | ((buf[2] & 0x80) ? 1 : 0);
props->cur_copr = ((buf[2] & 0x7F) << 2) | ((buf[3] & 0xC0) >> 6);
props->avg_copr = ((buf[3] & 0x3F) << 3) | ((buf[4] & 0xE0) >> 5);
props->s_cur_cnt = ((buf[4] & 0x1F) << 11) |
(buf[5] << 3) | ((buf[6] & 0xE0) >> 5);
props->s_avg_copr = ((buf[6] & 0x1F) << 4) | ((buf[7] & 0xF0) >> 4);
panel_dbg("copr_spi: cur_cnt %d, cur_copr %d, avg_copr %d, s_cur_cnt %d, s_avg_copr %d, copr_ready %d\n",
props->cur_cnt, props->cur_copr, props->avg_copr,
props->s_cur_cnt, props->s_avg_copr, props->copr_ready);
} else if (version == COPR_VER_0) {
props->cur_copr = buf[0];
}
get_copr_error:
kfree(buf);
return ret;
}
#else
static int panel_read_copr_dsi(struct copr_info *copr)
{
u8 *buf = NULL;
int i, c, index, ret, size;
struct panel_device *panel = to_panel_device(copr);
struct panel_info *panel_data;
struct copr_properties *props = &copr->props;
u32 version = get_copr_ver(copr);
int max_color = (version == COPR_VER_6) ?
MAX_RGBW_COLOR : MAX_COLOR;
if (unlikely(!panel)) {
panel_err("panel is null\n");
return -ENODEV;
}
panel_data = &panel->panel_data;
ret = panel_do_copr_seqtbl_by_index(copr, COPR_DSI_GET_SEQ);
if (unlikely(ret < 0)) {
panel_err("failed to do seqtbl\n");
ret = -EIO;
goto get_copr_error;
}
size = get_resource_size_by_name(panel_data, "copr_dsi");
if (size < 0) {
panel_err("failed to get copr size (ret %d)\n", size);
ret = -EINVAL;
goto get_copr_error;
}
buf = kzalloc(size, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto get_copr_error;
}
ret = resource_copy_by_name(panel_data, (u8 *)buf, "copr_dsi");
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
ret = -EIO;
goto get_copr_error;
}
if (version == COPR_VER_6) {
for (i = 0; i < 5; i++) {
for (c = 0; c < max_color; c++) {
index = i * (max_color * 2) + c * 2;
if (i == 4 && c == RGBW_WHITE) {
/* COPR_ROI5_W:8bit */
props->copr_roi_r[i][c] = buf[index];
} else {
/* COPR_ROI1~5 RGBW:10bit */
props->copr_roi_r[i][c] = (buf[index] << 8) | buf[index + 1];
}
}
panel_dbg("copr_dsi: copr_roi_r[%d] %d %d %d %d\n",
i, props->copr_roi_r[i][RGBW_RED],
props->copr_roi_r[i][RGBW_GREEN],
props->copr_roi_r[i][RGBW_BLUE],
props->copr_roi_r[i][RGBW_WHITE]);
}
} else if (version == COPR_VER_3 || version == COPR_VER_5) {
props->copr_ready = buf[0] & 0x01;
props->cur_cnt = (buf[1] << 8) | buf[2];
props->cur_copr = (buf[3] << 8) | buf[4];
props->avg_copr = (buf[5] << 8) | buf[6];
props->s_cur_cnt = (buf[7] << 8) | buf[8];
props->s_avg_copr = (buf[9] << 8) | buf[10];
for (i = 0; i < 5; i++) {
for (c = 0; c < max_color; c++) {
index = 11 + i * (max_color * 2) + c * 2;
props->copr_roi_r[i][c] = (buf[index] << 8) | buf[index + 1];
}
panel_dbg("copr_dsi: copr_roi_r[%d] %d %d %d\n",
i, props->copr_roi_r[i][RED],
props->copr_roi_r[i][GREEN],
props->copr_roi_r[i][BLUE]);
}
panel_dbg("copr_dsi: cur_cnt %d, cur_copr %d, avg_copr %d, s_cur_cnt %d, s_avg_copr %d, copr_ready %d\n",
props->cur_cnt, props->cur_copr, props->avg_copr,
props->s_cur_cnt, props->s_avg_copr, props->copr_ready);
} else if (version == COPR_VER_2) {
props->copr_ready = ((buf[10] & 0x80) ? 1 : 0);
props->cur_cnt = (buf[0] << 8) | buf[1];
props->cur_copr = (buf[2] << 8) | buf[3];
props->avg_copr = (buf[4] << 8) | buf[5];
props->s_cur_cnt = (buf[6] << 8) | buf[7];
props->s_avg_copr = (buf[8] << 8) | buf[9];
props->comp_copr = (((buf[10] & 0x01) ? 1 : 0) << 8) | buf[11];
panel_dbg("copr_dsi: cur_cnt %d, cur_copr %d, avg_copr %d, s_cur_cnt %d, s_avg_copr %d, copr_ready %d, comp_copr %d\n",
props->cur_cnt, props->cur_copr, props->avg_copr,
props->s_cur_cnt, props->s_avg_copr, props->copr_ready, props->comp_copr);
} else if (version == COPR_VER_1) {
props->copr_ready = ((buf[8] & 0x80) ? 1 : 0);
props->cur_cnt = (buf[0] << 8) | buf[1];
props->cur_copr = (buf[2] << 8) | buf[3];
props->avg_copr = (buf[4] << 8) | buf[5];
props->s_cur_cnt = (buf[6] << 8) | buf[7];
props->s_avg_copr = (buf[8] << 8) | buf[9];
panel_dbg("copr_dsi: cur_cnt %d, cur_copr %d, avg_copr %d, s_cur_cnt %d, s_avg_copr %d, copr_ready %d\n",
props->cur_cnt, props->cur_copr, props->avg_copr,
props->s_cur_cnt, props->s_avg_copr, props->copr_ready);
} else {
props->cur_copr = buf[0];
}
get_copr_error:
kfree(buf);
return ret;
}
#endif
static int panel_get_copr(struct copr_info *copr)
{
struct timespec64 cur_ts, last_ts, delta_ts;
struct copr_properties *props = &copr->props;
s64 elapsed_usec;
int ret;
if (unlikely(!props->support))
return -ENODEV;
ktime_get_ts64(&cur_ts);
if (props->state != COPR_REG_ON) {
panel_dbg("copr reg is not on state %d\n", props->state);
ret = -EINVAL;
goto get_copr_error;
}
if (atomic_read(&copr->stop)) {
panel_warn("copr_stop\n");
ret = -EINVAL;
goto get_copr_error;
}
#ifdef CONFIG_EXYNOS_DECON_LCD_SPI
panel_read_copr_spi(copr);
#else
panel_read_copr_dsi(copr);
#endif
ktime_get_ts64(&last_ts);
delta_ts = timespec64_sub(last_ts, cur_ts);
elapsed_usec = timespec64_to_ns(&delta_ts) / 1000;
panel_dbg("elapsed_usec %lld usec (%lld.%lld %lld.%lld)\n",
elapsed_usec, timespec64_to_ns(&cur_ts) / 1000000000,
(timespec64_to_ns(&cur_ts) % 1000000000) / 1000,
timespec64_to_ns(&last_ts) / 1000000000,
(timespec64_to_ns(&last_ts) % 1000000000) / 1000);
return 0;
get_copr_error:
return ret;
}
bool copr_is_enabled(struct copr_info *copr)
{
return (copr->props.support && get_copr_reg_copr_en(copr) && copr->props.enable);
}
static int copr_res_init(struct copr_info *copr)
{
timenval_init(&copr->res);
return 0;
}
static int copr_res_start(struct copr_info *copr, int value, struct timespec64 cur_ts)
{
timenval_start(&copr->res, value, cur_ts);
return 0;
}
int copr_update_start(struct copr_info *copr, int count)
{
if (unlikely(!copr->props.support))
return -ENODEV;
if (atomic_read(&copr->wq.count) < count)
atomic_set(&copr->wq.count, count);
wake_up_interruptible_all(&copr->wq.wait);
return 0;
}
int copr_update_average(struct copr_info *copr)
{
struct copr_properties *props = &copr->props;
struct timespec64 cur_ts;
int ret;
int cur_copr;
u32 version = get_copr_ver(copr);
if (unlikely(!copr->props.support))
return -ENODEV;
if (!copr_is_enabled(copr)) {
panel_dbg("copr disabled\n");
return -EIO;
}
ktime_get_ts64(&cur_ts);
if (props->state == COPR_UNINITIALIZED) {
panel_set_copr(copr);
panel_info("copr register updated\n");
}
ret = panel_get_copr(copr);
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
return -EINVAL;
}
if (version == COPR_VER_2 ||
version == COPR_VER_3 ||
version == COPR_VER_5 ||
version == COPR_VER_6) {
#ifdef CONFIG_SUPPORT_COPR_AVG
ret = panel_clear_copr(copr);
if (unlikely(ret < 0))
panel_err("failed to reset copr\n");
cur_copr = props->avg_copr;
timenval_update_average(&copr->res, cur_copr, cur_ts);
#else
cur_copr = props->cur_copr;
timenval_update_snapshot(&copr->res, cur_copr, cur_ts);
#endif
} else {
cur_copr = props->cur_copr;
timenval_update_snapshot(&copr->res, cur_copr, cur_ts);
}
return 0;
}
int copr_get_value(struct copr_info *copr)
{
struct copr_properties *props = &copr->props;
int ret;
int cur_copr;
if (unlikely(!copr->props.support))
return -ENODEV;
mutex_lock(&copr->lock);
if (!copr_is_enabled(copr)) {
panel_dbg("copr disabled\n");
mutex_unlock(&copr->lock);
return -EIO;
}
if (props->state == COPR_UNINITIALIZED) {
panel_set_copr(copr);
panel_info("copr register updated\n");
}
ret = panel_get_copr(copr);
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
mutex_unlock(&copr->lock);
return -EINVAL;
}
cur_copr = props->cur_copr;
mutex_unlock(&copr->lock);
return cur_copr;
}
int copr_iter_roi_get_value(struct copr_info *copr, struct copr_roi *roi, int size, u32 *out)
{
struct copr_properties *props = &copr->props;
struct timespec64 cur_ts;
int ret;
int i, c, cur_copr;
struct copr_reg reg;
u32 version = get_copr_ver(copr);
if (unlikely(!copr->props.support))
return -ENODEV;
/* update using last value or avg_copr */
mutex_lock(&copr->lock);
ret = copr_update_average(copr);
if (ret < 0) {
panel_err("failed to update average(ret %d)\n", ret);
mutex_unlock(&copr->lock);
return ret;
}
if (!copr_is_enabled(copr)) {
panel_dbg("copr disabled\n");
mutex_unlock(&copr->lock);
return -EIO;
}
panel_dbg("set roi\n");
memcpy(&reg, &copr->props.reg, sizeof(reg));
SET_COPR_REG_GAMMA(copr, 0);
if (version == COPR_VER_2) {
for (i = 0; i < size; i++) {
SET_COPR_REG_ROI(copr, &roi[i], 1);
SET_COPR_REG_E(copr, 0x300, 0, 0);
SET_COPR_REG_EC(copr, 0, 0x300, 0);
panel_set_copr(copr);
ret = panel_get_copr(copr);
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
/* restore r/g/b efficiency & roi */
memcpy(&copr->props.reg, &reg, sizeof(copr->props.reg));
mutex_unlock(&copr->lock);
return -EINVAL;
}
out[i * 3 + 0] = props->cur_copr;
out[i * 3 + 1] = props->comp_copr;
SET_COPR_REG_E(copr, 0, 0, 0x300);
SET_COPR_REG_EC(copr, 0, 0, 0);
panel_set_copr(copr);
ret = panel_get_copr(copr);
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
/* restore r/g/b efficiency & roi */
memcpy(&copr->props.reg, &reg, sizeof(copr->props.reg));
mutex_unlock(&copr->lock);
return -EINVAL;
}
out[i * 3 + 2] = props->cur_copr;
}
} else if (version == COPR_VER_1) {
for (i = 0; i < size; i++) {
for (c = 0; c < MAX_COLOR; c++) {
SET_COPR_REG_ROI(copr, &roi[i], 1);
if (c == 0)
SET_COPR_REG_E(copr, 0x300, 0, 0);
else if (c == 1)
SET_COPR_REG_E(copr, 0, 0x300, 0);
else if (c == 2)
SET_COPR_REG_E(copr, 0, 0, 0x300);
panel_set_copr(copr);
ret = panel_get_copr(copr);
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
/* restore r/g/b efficiency & roi */
memcpy(&copr->props.reg, &reg, sizeof(copr->props.reg));
mutex_unlock(&copr->lock);
return -EINVAL;
}
out[i * 3 + c] = props->cur_copr;
}
}
}
/* restore r/g/b efficiency & roi */
memcpy(&copr->props.reg, &reg, sizeof(copr->props.reg));
#ifdef CONFIG_SUPPORT_COPR_AVG
if (version == COPR_VER_2 ||
version == COPR_VER_1) {
ret = panel_do_copr_seqtbl_by_index(copr, COPR_CLR_CNT_ON_SEQ);
if (unlikely(ret < 0))
panel_err("failed to do seqtbl\n");
msleep(34);
}
#endif
panel_set_copr(copr);
msleep(34);
ret = panel_get_copr(copr);
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
mutex_unlock(&copr->lock);
return -EINVAL;
}
panel_dbg("restore roi\n");
/*
* exclude elapsed time of copr roi snapshot reading
* in copr_sum_update. so update last_ts as cur_ts.
*/
ktime_get_ts64(&cur_ts);
cur_copr = props->cur_copr;
copr_res_start(copr, cur_copr, cur_ts);
mutex_unlock(&copr->lock);
return 0;
}
int copr_cur_roi_get_value(struct copr_info *copr, struct copr_roi *roi, int size, u32 *out)
{
struct copr_properties *props = &copr->props;
int i, c, max_size = 5, ret;
int max_color = (props->version == COPR_VER_6) ?
MAX_RGBW_COLOR : MAX_COLOR;
if (unlikely(!copr->props.support))
return -ENODEV;
mutex_lock(&copr->lock);
if (!copr_is_enabled(copr)) {
panel_dbg("copr disabled\n");
mutex_unlock(&copr->lock);
return -EIO;
}
if (props->state == COPR_UNINITIALIZED) {
SET_COPR_REG_ROI(copr, roi, (int)min(size, max_size));
panel_set_copr(copr);
panel_info("copr register updated\n");
}
ret = panel_get_copr(copr);
if (ret < 0) {
panel_err("failed to get copr (ret %d)\n", ret);
mutex_unlock(&copr->lock);
return -EINVAL;
}
for (i = 0; i < (int)min(size, max_size); i++)
for (c = 0; c < max_color; c++)
out[i * max_color + c] = props->copr_roi_r[i][c];
mutex_unlock(&copr->lock);
return 0;
}
int copr_roi_set_value(struct copr_info *copr, struct copr_roi *roi, int size)
{
if (unlikely(!copr->props.support))
return -ENODEV;
if (!copr_is_enabled(copr)) {
panel_dbg("copr disabled\n");
return -EIO;
}
mutex_lock(&copr->lock);
SET_COPR_REG_ROI(copr, roi, (int)min(size, 6));
panel_set_copr(copr);
mutex_unlock(&copr->lock);
return 0;
}
/**
* copr_roi_get_value - get copr value of each roi
*
* @ copr : copr_info
* @ roi : a pointer of roi array.
* @ size : size of roi array.
* @ out : a pointer roi output value to be stored.
*
* Get copr snapshot for each roi's r/g/b.
* This function returns 0 if the average is valid.
* If not this function returns -ERRNO.
*/
int copr_roi_get_value(struct copr_info *copr, struct copr_roi *roi, int size, u32 *out)
{
u32 version = get_copr_ver(copr);
if (version > COPR_VER_2 ||
version == COPR_VER_5 ||
version == COPR_VER_6)
return copr_cur_roi_get_value(copr, roi, size, out);
else
return copr_iter_roi_get_value(copr, roi, size, out);
}
int copr_clear_average(struct copr_info *copr)
{
timenval_clear_average(&copr->res);
return 0;
}
/**
* copr_get_average_and_clear - get copr average.
*
* @ copr : copr_info
* @ index : a index of copr result.
*
* Get copr average from sum and elapsed_msec.
* And clears copr_res's sum and elapsed_msec variables.
* This function returns 0 if the average is valid.
* If not this function returns -ERRNO.
*/
int copr_get_average_and_clear(struct copr_info *copr)
{
int avg;
if (unlikely(!copr->props.support))
return -ENODEV;
mutex_lock(&copr->lock);
copr_update_average(copr);
avg = copr->res.avg;
copr_clear_average(copr);
mutex_unlock(&copr->lock);
return avg;
}
#ifdef CONFIG_EXYNOS_DECON_LCD_SPI
static int set_spi_gpios(struct panel_device *panel, int en)
{
int err_num = 0;
struct spi_device *spi = panel->spi;
struct copr_spi_gpios *gpio_info = &panel->spi_gpio;
if (!spi) {
panel_dbg("spi is null\n");
return 0;
}
panel_info("en : %d\n", en);
if (en) {
if (gpio_direction_output(gpio_info->gpio_sck, 0))
goto set_exit;
err_num++;
if (gpio_direction_input(gpio_info->gpio_miso))
goto set_exit;
err_num++;
if (gpio_direction_output(gpio_info->gpio_mosi, 0))
goto set_exit;
err_num++;
if (gpio_direction_output(gpio_info->gpio_cs, 0))
goto set_exit;
} else {
if (gpio_direction_input(gpio_info->gpio_sck))
goto set_exit;
err_num++;
if (gpio_direction_input(gpio_info->gpio_miso))
goto set_exit;
err_num++;
if (gpio_direction_input(gpio_info->gpio_mosi))
goto set_exit;
err_num++;
if (gpio_direction_input(gpio_info->gpio_cs))
goto set_exit;
}
return 0;
set_exit:
panel_err("failed to gpio:%d\n", err_num);
return -EIO;
}
static int get_spi_gpios_dt(struct panel_device *panel)
{
int ret = 0;
struct device_node *np;
struct spi_device *spi = panel->spi;
struct copr_spi_gpios *gpio_info = &panel->spi_gpio;
if (!spi) {
panel_err("spi or gpio_info is null\n");
goto error_dt;
}
np = spi->master->dev.of_node;
if (!np) {
panel_err("dev_of_node is null\n");
goto error_dt;
}
gpio_info->gpio_sck = of_get_named_gpio(np, "gpio-sck", 0);
if (gpio_info->gpio_sck < 0) {
panel_err("failed to get gpio_sck from dt\n");
goto error_dt;
}
gpio_info->gpio_miso = of_get_named_gpio(np, "gpio-miso", 0);
if (gpio_info->gpio_miso < 0) {
panel_err("failed to get miso from dt\n");
goto error_dt;
}
gpio_info->gpio_mosi = of_get_named_gpio(np, "gpio-mosi", 0);
if (gpio_info->gpio_mosi < 0) {
panel_err("failed to get mosi from dt\n");
goto error_dt;
}
gpio_info->gpio_cs = of_get_named_gpio(np, "cs-gpios", 0);
if (gpio_info->gpio_cs < 0) {
panel_err("failed to get cs from dt\n");
goto error_dt;
}
error_dt:
return ret;
}
#endif
int copr_enable(struct copr_info *copr)
{
struct panel_device *panel = to_panel_device(copr);
struct panel_state *state = &panel->state;
struct copr_properties *props = &copr->props;
int ret;
u32 version = get_copr_ver(copr);
if (unlikely(!copr->props.support))
return -ENODEV;
if (copr_is_enabled(copr)) {
panel_info("already enabled\n");
return 0;
}
#ifdef CONFIG_EXYNOS_DECON_LCD_SPI
if (set_spi_gpios(panel, 1))
panel_err("failed to set spio gpio\n");
#endif
panel_info("+\n");
atomic_set(&copr->stop, 0);
mutex_lock(&copr->lock);
copr->props.enable = true;
if (state->disp_on == PANEL_DISPLAY_ON) {
/*
* TODO : check whether "copr-set" is includued in "init-seq".
* If COPR_SET_SEQ is included in INIT_SEQ, set state COPR_REG_ON.
* If not, copr state should be COPR_UNINITIALIZED.
*/
props->state = COPR_REG_ON;
}
if (copr->props.options.check_avg) {
copr_res_init(copr);
#ifdef CONFIG_SUPPORT_COPR_AVG
if (version == COPR_VER_1 ||
version == COPR_VER_2 ||
version == COPR_VER_3 ||
version == COPR_VER_5 ||
version == COPR_VER_6) {
ret = panel_clear_copr(copr);
if (unlikely(ret < 0))
panel_err("failed to reset copr\n");
}
#endif
copr_update_average(copr);
}
mutex_unlock(&copr->lock);
panel_info("-\n");
#ifdef CONFIG_PANEL_NOTIFY
panel_send_coprstate_notify(PANEL_EVENT_COPR_ENABLED);
#endif
return 0;
}
int copr_disable(struct copr_info *copr)
{
#ifdef CONFIG_EXYNOS_DECON_LCD_SPI
struct panel_device *panel = to_panel_device(copr);
#endif
struct copr_properties *props = &copr->props;
if (unlikely(!copr->props.support))
return -ENODEV;
if (!copr_is_enabled(copr)) {
panel_info("already disabled\n");
return 0;
}
panel_info("+\n");
atomic_set(&copr->stop, 1);
mutex_lock(&copr->lock);
if (copr->props.options.check_avg) {
if (get_copr_ver(copr) < COPR_VER_2)
copr_update_average(copr);
}
if (props->enable) {
props->enable = false;
props->state = COPR_UNINITIALIZED;
}
mutex_unlock(&copr->lock);
#ifdef CONFIG_EXYNOS_DECON_LCD_SPI
if (set_spi_gpios(panel, 0))
panel_err("failed to set spio gpio\n");
#endif
panel_info("-\n");
#ifdef CONFIG_PANEL_NOTIFY
panel_send_coprstate_notify(PANEL_EVENT_COPR_DISABLED);
#endif
return 0;
}
static int copr_thread(void *data)
{
struct copr_info *copr = data;
int last_value;
int ret;
bool should_stop = false;
#ifdef CONFIG_SUPPORT_COPR_AVG
bool runnable = (get_copr_ver(copr) < COPR_VER_2);
#else
bool runnable = true;
#endif
last_value = copr->res.last_value;
while (!kthread_should_stop()) {
ret = wait_event_interruptible(copr->wq.wait,
(should_stop = copr->wq.should_stop || kthread_should_stop()) ||
((atomic_read(&copr->wq.count) > 0) &&
copr_is_enabled(copr) && runnable));
if (should_stop)
break;
if (last_value != copr->res.last_value)
atomic_dec(&copr->wq.count);
if (!ret) {
mutex_lock(&copr->lock);
copr_update_average(copr);
last_value = copr->res.last_value;
mutex_unlock(&copr->lock);
usleep_range(16660, 16670);
}
}
return 0;
}
static int copr_create_thread(struct copr_info *copr)
{
if (unlikely(!copr->props.support)) {
panel_warn("copr unsupported\n");
return 0;
}
copr->wq.should_stop = false;
copr->wq.thread = kthread_run(copr_thread, copr, "copr-thread");
if (IS_ERR_OR_NULL(copr->wq.thread)) {
panel_err("failed to run copr thread\n");
copr->wq.thread = NULL;
return PTR_ERR(copr->wq.thread);
}
return 0;
}
static int copr_destroy_thread(struct copr_info *copr)
{
if (unlikely(!copr->props.support)) {
panel_warn("copr unsupported\n");
return 0;
}
if (IS_ERR_OR_NULL(copr->wq.thread))
return 0;
copr->wq.should_stop = true;
/* wake up waitqueue to stop */
wake_up_interruptible_all(&copr->wq.wait);
/* kthread_should_stop() == true */
kthread_stop(copr->wq.thread);
return 0;
}
static int copr_fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data)
{
struct copr_info *copr;
struct fb_event *evdata = data;
int fb_blank;
int early_blank;
switch (event) {
case FB_EVENT_BLANK:
early_blank = 0;
break;
default:
return 0;
}
copr = container_of(self, struct copr_info, fb_notif);
fb_blank = *(int *)evdata->data;
panel_dbg("fb_blank:%d\n", fb_blank);
if (evdata->info->node != 0)
return 0;
if (unlikely(!copr->props.support))
return 0;
return 0;
}
static int copr_register_fb(struct copr_info *copr)
{
memset(&copr->fb_notif, 0, sizeof(copr->fb_notif));
copr->fb_notif.notifier_call = copr_fb_notifier_callback;
return fb_register_client(&copr->fb_notif);
}
static int copr_unregister_fb(struct copr_info *copr)
{
fb_unregister_client(&copr->fb_notif);
copr->fb_notif.notifier_call = NULL;
return 0;
}
int copr_probe(struct panel_device *panel, struct panel_copr_data *copr_data)
{
struct copr_info *copr;
int i;
if (!panel || !copr_data) {
panel_err("panel(%p) or copr_data(%p) not exist\n", panel, copr_data);
return -EINVAL;
}
copr = &panel->copr;
atomic_set(&copr->stop, 0);
mutex_lock(&copr->lock);
memcpy(&copr->props.reg, &copr_data->reg, sizeof(struct copr_reg));
copr->props.version = copr_data->version;
memcpy(&copr->props.options, &copr_data->options, sizeof(struct copr_options));
memcpy(&copr->props.roi, &copr_data->roi, sizeof(copr->props.roi));
copr->props.nr_roi = copr_data->nr_roi;
copr->seqtbl = copr_data->seqtbl;
copr->nr_seqtbl = copr_data->nr_seqtbl;
copr->maptbl = copr_data->maptbl;
copr->nr_maptbl = copr_data->nr_maptbl;
for (i = 0; i < copr->nr_maptbl; i++)
copr->maptbl[i].pdata = copr;
init_waitqueue_head(&copr->wq.wait);
copr->props.support = true;
copr_register_fb(copr);
#ifdef CONFIG_EXYNOS_DECON_LCD_SPI
get_spi_gpios_dt(panel);
#endif
for (i = 0; i < copr->nr_maptbl; i++)
maptbl_init(&copr->maptbl[i]);
if (IS_PANEL_ACTIVE(panel) &&
get_copr_reg_copr_en(copr)) {
copr->props.enable = true;
copr->props.state = COPR_UNINITIALIZED;
panel_set_copr(copr);
atomic_set(&copr->wq.count, 5);
if (copr->props.options.thread_on)
copr_create_thread(copr);
}
mutex_unlock(&copr->lock);
panel_info("registered successfully\n");
return 0;
}
int copr_remove(struct panel_device *panel)
{
struct copr_info *copr;
if (!panel) {
panel_err("panel(%p) not exist\n", panel);
return -EINVAL;
}
copr = &panel->copr;
if (copr->props.options.thread_on)
copr_destroy_thread(copr);
mutex_lock(&copr->lock);
copr->props.support = false;
copr_unregister_fb(copr);
mutex_unlock(&copr->lock);
return 0;
}