Merge branch 'partner/android-exynos-r11-4.19-rvc-jr3' into android-exynos-r11-4.19
JUL 2023.1
Bug: 280774281
Change-Id: I2b5558abf36662bf0a5bcdd9e02f113027b16012
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 337e177..252d13b 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -468,9 +468,19 @@
* (protected by binder_deferred_lock)
* @deferred_work: bitmap of deferred work to perform
* (protected by binder_deferred_lock)
+ * @outstanding_txns: number of transactions to be transmitted before
+ * processes in freeze_wait are woken up
+ * (protected by @inner_lock)
* @is_dead: process is dead and awaiting free
* when outstanding transactions are cleaned up
* (protected by @inner_lock)
+ * @sync_recv: process received sync transactions since last frozen
+ * (protected by @inner_lock)
+ * @async_recv: process received async transactions since last frozen
+ * (protected by @inner_lock)
+ * @freeze_wait: waitqueue of processes waiting for all outstanding
+ * transactions to be processed
+ * (protected by @inner_lock)
* @todo: list of work for this process
* (protected by @inner_lock)
* @stats: per-process binder statistics
@@ -514,7 +524,12 @@
const struct cred *cred;
struct hlist_node deferred_work_node;
int deferred_work;
+ int outstanding_txns;
bool is_dead;
+ bool is_frozen;
+ bool sync_recv;
+ bool async_recv;
+ wait_queue_head_t freeze_wait;
struct list_head todo;
struct binder_stats stats;
@@ -2087,6 +2102,10 @@
if (target_proc) {
binder_inner_proc_lock(target_proc);
+ target_proc->outstanding_txns--;
+ BUG_ON(target_proc->outstanding_txns < 0);
+ if (!target_proc->outstanding_txns && target_proc->is_frozen)
+ wake_up_interruptible_all(&target_proc->freeze_wait);
if (t->buffer)
t->buffer->transaction = NULL;
binder_inner_proc_unlock(target_proc);
@@ -2837,10 +2856,11 @@
* If the @thread parameter is not NULL, the transaction is always queued
* to the waitlist of that specific thread.
*
- * Return: true if the transactions was successfully queued
- * false if the target process or thread is dead
+ * Return: 0 if the transaction was successfully queued
+ * BR_DEAD_REPLY if the target process or thread is dead
+ * BR_FROZEN_REPLY if the target process or thread is frozen
*/
-static bool binder_proc_transaction(struct binder_transaction *t,
+static int binder_proc_transaction(struct binder_transaction *t,
struct binder_proc *proc,
struct binder_thread *thread)
{
@@ -2864,11 +2884,18 @@
}
binder_inner_proc_lock(proc);
+ if (proc->is_frozen) {
+ proc->sync_recv |= !oneway;
+ proc->async_recv |= oneway;
+ }
- if (proc->is_dead || (thread && thread->is_dead)) {
+ if ((proc->is_frozen && !oneway) || proc->is_dead ||
+ (thread && thread->is_dead)) {
+ bool proc_is_dead = proc->is_dead
+ || (thread && thread->is_dead);
binder_inner_proc_unlock(proc);
binder_node_unlock(node);
- return false;
+ return proc_is_dead ? BR_DEAD_REPLY : BR_FROZEN_REPLY;
}
if (!thread && !pending_async)
@@ -2887,10 +2914,11 @@
if (!pending_async)
binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);
+ proc->outstanding_txns++;
binder_inner_proc_unlock(proc);
binder_node_unlock(node);
- return true;
+ return 0;
}
/**
@@ -3508,13 +3536,16 @@
if (reply) {
binder_enqueue_thread_work(thread, tcomplete);
binder_inner_proc_lock(target_proc);
- if (target_thread->is_dead) {
+ if (target_thread->is_dead || target_proc->is_frozen) {
+ return_error = target_thread->is_dead ?
+ BR_DEAD_REPLY : BR_FROZEN_REPLY;
binder_inner_proc_unlock(target_proc);
goto err_dead_proc_or_thread;
}
BUG_ON(t->buffer->async_transaction != 0);
binder_pop_transaction_ilocked(target_thread, in_reply_to);
binder_enqueue_thread_work_ilocked(target_thread, &t->work);
+ target_proc->outstanding_txns++;
binder_inner_proc_unlock(target_proc);
wake_up_interruptible_sync(&target_thread->wait);
binder_restore_priority(current, in_reply_to->saved_priority);
@@ -3534,7 +3565,9 @@
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
binder_inner_proc_unlock(proc);
- if (!binder_proc_transaction(t, target_proc, target_thread)) {
+ return_error = binder_proc_transaction(t,
+ target_proc, target_thread);
+ if (return_error) {
binder_inner_proc_lock(proc);
binder_pop_transaction_ilocked(thread, t);
binder_inner_proc_unlock(proc);
@@ -3544,7 +3577,8 @@
BUG_ON(target_node == NULL);
BUG_ON(t->buffer->async_transaction != 1);
binder_enqueue_thread_work(thread, tcomplete);
- if (!binder_proc_transaction(t, target_proc, NULL))
+ return_error = binder_proc_transaction(t, target_proc, NULL);
+ if (return_error)
goto err_dead_proc_or_thread;
}
if (target_thread)
@@ -3561,7 +3595,6 @@
return;
err_dead_proc_or_thread:
- return_error = BR_DEAD_REPLY;
return_error_line = __LINE__;
binder_dequeue_work(proc, tcomplete);
err_translate_failed:
@@ -4167,7 +4200,7 @@
binder_inner_proc_lock(proc);
list_del_init(&thread->waiting_thread_node);
if (signal_pending(current)) {
- ret = -ERESTARTSYS;
+ ret = -EINTR;
break;
}
}
@@ -4671,6 +4704,7 @@
BUG_ON(!list_empty(&proc->todo));
BUG_ON(!list_empty(&proc->delivered_death));
+ WARN_ON(proc->outstanding_txns);
device = container_of(proc->context, struct binder_device, context);
if (refcount_dec_and_test(&device->ref)) {
kfree(proc->context->name);
@@ -4732,6 +4766,7 @@
(t->to_thread == thread) ? "in" : "out");
if (t->to_thread == thread) {
+ t->to_proc->outstanding_txns--;
t->to_proc = NULL;
t->to_thread = NULL;
if (t->buffer) {
@@ -4975,6 +5010,76 @@
return 0;
}
+static int binder_ioctl_freeze(struct binder_freeze_info *info,
+ struct binder_proc *target_proc)
+{
+ int ret = 0;
+
+ if (!info->enable) {
+ binder_inner_proc_lock(target_proc);
+ target_proc->sync_recv = false;
+ target_proc->async_recv = false;
+ target_proc->is_frozen = false;
+ binder_inner_proc_unlock(target_proc);
+ return 0;
+ }
+
+ /*
+ * Freezing the target. Prevent new transactions by
+ * setting frozen state. If timeout specified, wait
+ * for transactions to drain.
+ */
+ binder_inner_proc_lock(target_proc);
+ target_proc->sync_recv = false;
+ target_proc->async_recv = false;
+ target_proc->is_frozen = true;
+ binder_inner_proc_unlock(target_proc);
+
+ if (info->timeout_ms > 0)
+ ret = wait_event_interruptible_timeout(
+ target_proc->freeze_wait,
+ (!target_proc->outstanding_txns),
+ msecs_to_jiffies(info->timeout_ms));
+
+ if (!ret && target_proc->outstanding_txns)
+ ret = -EAGAIN;
+
+ if (ret < 0) {
+ binder_inner_proc_lock(target_proc);
+ target_proc->is_frozen = false;
+ binder_inner_proc_unlock(target_proc);
+ }
+
+ return ret;
+}
+
+static int binder_ioctl_get_freezer_info(
+ struct binder_frozen_status_info *info)
+{
+ struct binder_proc *target_proc;
+ bool found = false;
+
+ info->sync_recv = 0;
+ info->async_recv = 0;
+
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
+ if (target_proc->pid == info->pid) {
+ found = true;
+ binder_inner_proc_lock(target_proc);
+ info->sync_recv |= target_proc->sync_recv;
+ info->async_recv |= target_proc->async_recv;
+ binder_inner_proc_unlock(target_proc);
+ }
+ }
+ mutex_unlock(&binder_procs_lock);
+
+ if (!found)
+ return -EINVAL;
+
+ return 0;
+}
+
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
@@ -5093,6 +5198,84 @@
}
break;
}
+ case BINDER_FREEZE: {
+ struct binder_freeze_info info;
+ struct binder_proc **target_procs = NULL, *target_proc;
+ int target_procs_count = 0, i = 0;
+
+ ret = 0;
+
+ if (copy_from_user(&info, ubuf, sizeof(info))) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ mutex_lock(&binder_procs_lock);
+ hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
+ if (target_proc->pid == info.pid)
+ target_procs_count++;
+ }
+
+ if (target_procs_count == 0) {
+ mutex_unlock(&binder_procs_lock);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ target_procs = kmalloc(sizeof(struct binder_proc *) *
+ target_procs_count,
+ GFP_KERNEL);
+
+ if (!target_procs) {
+ mutex_unlock(&binder_procs_lock);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ hlist_for_each_entry(target_proc, &binder_procs, proc_node) {
+ if (target_proc->pid != info.pid)
+ continue;
+
+ binder_inner_proc_lock(target_proc);
+ target_proc->tmp_ref++;
+ binder_inner_proc_unlock(target_proc);
+
+ target_procs[i++] = target_proc;
+ }
+ mutex_unlock(&binder_procs_lock);
+
+ for (i = 0; i < target_procs_count; i++) {
+ if (ret >= 0)
+ ret = binder_ioctl_freeze(&info,
+ target_procs[i]);
+
+ binder_proc_dec_tmpref(target_procs[i]);
+ }
+
+ kfree(target_procs);
+
+ if (ret < 0)
+ goto err;
+ break;
+ }
+ case BINDER_GET_FROZEN_INFO: {
+ struct binder_frozen_status_info info;
+
+ if (copy_from_user(&info, ubuf, sizeof(info))) {
+ ret = -EFAULT;
+ goto err;
+ }
+
+ ret = binder_ioctl_get_freezer_info(&info);
+ if (ret < 0)
+ goto err;
+
+ if (copy_to_user(ubuf, &info, sizeof(info))) {
+ ret = -EFAULT;
+ goto err;
+ }
+ break;
+ }
default:
ret = -EINVAL;
goto err;
@@ -5102,7 +5285,7 @@
if (thread)
thread->looper_need_return = false;
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
- if (ret && ret != -ERESTARTSYS)
+ if (ret && ret != -EINTR)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
@@ -5207,6 +5390,7 @@
mutex_init(&proc->files_lock);
proc->cred = get_cred(filp->f_cred);
INIT_LIST_HEAD(&proc->todo);
+ init_waitqueue_head(&proc->freeze_wait);
if (binder_supported_policy(current->policy)) {
proc->default_priority.sched_policy = current->policy;
proc->default_priority.prio = current->normal_prio;
@@ -5428,6 +5612,9 @@
proc->tmp_ref++;
proc->is_dead = true;
+ proc->is_frozen = false;
+ proc->sync_recv = false;
+ proc->async_recv = false;
threads = 0;
active_transactions = 0;
while ((n = rb_first(&proc->threads))) {
diff --git a/drivers/video/fbdev/exynos/dpu30/panels/panel-boe-wf012fbm.c b/drivers/video/fbdev/exynos/dpu30/panels/panel-boe-wf012fbm.c
index 2e60e6f..1f7a1d1 100644
--- a/drivers/video/fbdev/exynos/dpu30/panels/panel-boe-wf012fbm.c
+++ b/drivers/video/fbdev/exynos/dpu30/panels/panel-boe-wf012fbm.c
@@ -19,6 +19,28 @@
#define PANEL_MAX_TRIES 3
+// Look up table to convert between AOD and Interactive DBV that would
+// yield the same display nits, defined in go/rh-eos-dbv-lut
+const static u8 InteractiveToAodDbvLUT[] = {
+ 0, 3, 5, 5, 8, 15, 16, 21, 25, 30, 33, 38, 43,
+ 47, 51, 56, 59, 69, 69, 72, 77, 79, 81, 83, 86, 88,
+ 90, 92, 94, 96, 98, 101, 103, 106, 110, 112, 114, 116, 120,
+ 122, 125, 128, 130, 134, 136, 140, 143, 146, 149, 152, 155, 158,
+ 162, 166, 169, 174, 178, 182, 186, 188, 192, 196, 201, 205, 211,
+ 215, 220, 225, 228, 232, 237, 241, 246, 251
+};
+
+static u8 interactive_to_aod_brightness(u8 interactive_brightness)
+{
+ if (interactive_brightness >= sizeof(InteractiveToAodDbvLUT)) {
+ // This means Interactive brightness (nits) is already saturated
+ // if converted to Doze brightness (i.e. > max AOD 150 nits).
+ // Let's return the maximum DBV here, which is 255.
+ return 255;
+ }
+ return InteractiveToAodDbvLUT[interactive_brightness];
+}
+
static int wf012fbm_suspend(struct exynos_panel_device *panel)
{
struct dsim_device *dsim = get_dsim_drvdata(0);
@@ -81,6 +103,19 @@
dsim_write_data_seq(dsim, false, 0x57, 0x30);
dsim_write_data_seq(dsim, false, 0xff, 0x10);
+ /* Disable master DBV control mode */
+ dsim_write_data_seq(dsim, false, 0xff, 0x21);
+ dsim_write_data_seq(dsim, false, 0xfb, 0x01);
+ dsim_write_data_seq(dsim, false, 0x5d, 0x00);
+ dsim_write_data_seq(dsim, false, 0xff, 0x10);
+
+ /* No gradual DBV change during the mode transition */
+ dsim_write_data_seq(dsim, false, 0xff, 0x25);
+ dsim_write_data_seq(dsim, false, 0xfb, 0x01);
+ dsim_write_data_seq(dsim, false, 0x74, 0xa1);
+ dsim_write_data_seq(dsim, false, 0x76, 0x51);
+ dsim_write_data_seq(dsim, false, 0xff, 0x10);
+
/* Write sequence for setting the PWM frequency if necessary*/
ret = dsim_read_data(dsim, MIPI_DSI_DCS_READ,
0xDA, sizeof(buf), buf);
@@ -350,50 +385,13 @@
static int wf012fbm_doze(struct exynos_panel_device *panel)
{
struct dsim_device *dsim = get_dsim_drvdata(0);
- u8 aod_brightness = 0;
- u8 interactive_brightness = 0;
- u16 brightness_u16 = 0;
DPU_INFO_PANEL("%s +\n", __func__);
mutex_lock(&panel->ops_lock);
/* Page select */
dsim_write_data_seq(dsim, false, 0xff, 0x10);
- /* Calculate normalized brightness */
- interactive_brightness = panel->bl->props.brightness;
- if (interactive_brightness > 0) {
- brightness_u16 =
- DIV_ROUND_CLOSEST(interactive_brightness * 650, 150);
- /* If normalized brightness is set late after mode-change
- frame finish, it could cause a dip when interactive brightness
- gets scaled down (150/650) and rises back up to the normalized
- level. To cover for this case, let's scale down the normalized
- brightness by 90% so that it doesn't rise back up. This should
- only apply to brighter level (i.e. > 20/255) where the dip
- could be seen easily. */
- if (interactive_brightness > 20) {
- brightness_u16 = brightness_u16 * 90 / 100;
- }
- aod_brightness = brightness_u16 > 255 ? 255 : brightness_u16;
- }
-
/* Idle Mode */
dsim_write_data_seq(dsim, false, MIPI_DCS_ENTER_IDLE_MODE);
- /* Set normalized brightness */
- if (!panel->doze_brightness_normalized && aod_brightness != 0) {
- /* Delay until mode-change frame.
- This requires an exact delay of 1 frame after exit-doze cmd */
- usleep_range(16667, 16667);
- /* Disable brightness dimming */
- dsim_write_data_seq(dsim, false, 0x53, 0x20);
- /* Set normalized brightness */
- dsim_write_data_seq(dsim, false, 0x51, aod_brightness);
- /* Delay until mode-change frame ends */
- usleep_range(16667, 16667);
- /* Enable brightness dimming */
- dsim_write_data_seq(dsim, false, 0x53, 0x28);
- panel->doze_brightness_normalized = true;
- }
-
/* Exit HBM in case it's on */
dsim_write_data_seq(dsim, false, 0x66, 0x00);
@@ -409,7 +407,8 @@
static int wf012fbm_set_light(struct exynos_panel_device *panel, u32 br_val)
{
- u8 data;
+ u8 interactive_brightness;
+ u8 aod_brightness;
struct dsim_device *dsim = get_dsim_drvdata(0);
/*
* Only set brightness if it's not currently in doze mode as AP
@@ -424,9 +423,14 @@
if (dsim->state != DSIM_STATE_DOZE) {
mutex_lock(&panel->ops_lock);
/* WRDISBV(8bit): 1st DBV[7:0] */
- data = br_val & 0xFF;
- dsim_write_data_seq(dsim, false, 0x51, data);
+ interactive_brightness = br_val & 0xFF;
+ aod_brightness =
+ interactive_to_aod_brightness(interactive_brightness);
+ dsim_write_data_seq(dsim, false, 0x51, interactive_brightness);
+ dsim_write_data_seq(dsim, false, 0x61, aod_brightness);
mutex_unlock(&panel->ops_lock);
+ DPU_INFO_PANEL("%s: set brightness (i=%d, aod=%d)\n", __func__,
+ interactive_brightness, aod_brightness);
} else {
DPU_INFO_PANEL("%s: skip doze br=%d\n", __func__, br_val);
}
@@ -466,50 +470,22 @@
static int wf012fbm_exit_doze(struct exynos_panel_device *panel)
{
struct dsim_device *dsim = get_dsim_drvdata(0);
- unsigned char buf[1] = {0};
- u8 normalized_brightness = 0;
+ u8 brightness = 0;
DPU_INFO_PANEL("%s +\n", __func__);
mutex_lock(&panel->ops_lock);
+ /* page select 0x10 */
dsim_write_data_seq(dsim, false, 0xff, 0x10);
- /* Read brightness */
- dsim_read_data(dsim, MIPI_DSI_DCS_READ,
- MIPI_DCS_GET_DISPLAY_BRIGHTNESS, sizeof(buf), buf);
- if (buf[0] != 0) {
- panel->bl->props.brightness = buf[0];
- /* Calculate interactive DBV that would yield the same
- brightness nits to the current AOD DBV.
- As both interactive and AOD DBV-nits conversion are linear,
- this can be done by scaling with the max brightness nits
- (150 nits for AOD and 650 nits for interactive at DBV255) */
- normalized_brightness = DIV_ROUND_CLOSEST(buf[0] * 150, 650);
- /* If normalized brightness is set late after mode-change
- frame finish, it could cause a spike when AOD brightness gets
- scaled up (650/150) and dip back down to the normalized level.
- To cover for this case, let's scale up the normalized
- brightness by 120% so that it doesn't dip back down */
- normalized_brightness = normalized_brightness * 120 / 100;
+ /* Read interactive brightness */
+ dsim_read_data(dsim, MIPI_DSI_DCS_READ, 0x51,
+ sizeof(brightness), &brightness);
+ if (brightness != 0) {
+ panel->bl->props.brightness = brightness;
}
/* Exit Idle Mode */
dsim_write_data_seq(dsim, false, MIPI_DCS_EXIT_IDLE_MODE);
- /* Flatten mode-change brightness ramp */
- if (normalized_brightness != 0) {
- panel->bl->props.brightness = normalized_brightness;
- /* Delay until mode-change frame.
- This requires an exact delay of 1 frame after exit-doze cmd */
- usleep_range(16667, 16667);
- /* Disable brightness dimming */
- dsim_write_data_seq(dsim, false, 0x53, 0x20);
- /* Set normalized brightness */
- dsim_write_data_seq(dsim, false, 0x51, normalized_brightness);
- /* Delay until mode-change frame ends */
- usleep_range(16667, 16667);
- /* Enable brightness dimming */
- dsim_write_data_seq(dsim, false, 0x53, 0x28);
- }
- panel->doze_brightness_normalized = false;
mutex_unlock(&panel->ops_lock);
- DPU_INFO_PANEL("%s -\n", __func__);
+ DPU_INFO_PANEL("%s - read br=%d\n", __func__, brightness);
return 0;
}
diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
index f02aef1..4ad24d0 100644
--- a/fs/tracefs/inode.c
+++ b/fs/tracefs/inode.c
@@ -517,7 +517,8 @@
if (unlikely(!inode))
return failed_creating(dentry);
- inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
+ /* Do not set bits for OTH */
+ inode->i_mode = S_IFDIR | S_IRWXU | S_IRUSR| S_IRGRP | S_IXUSR | S_IXGRP;
inode->i_op = ops;
inode->i_fop = &simple_dir_operations;
inode->i_uid = d_inode(dentry->d_parent)->i_uid;
diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h
index 36a53fd..d5bcba6 100644
--- a/include/uapi/linux/android/binder.h
+++ b/include/uapi/linux/android/binder.h
@@ -264,6 +264,18 @@
__u32 reserved3;
};
+struct binder_freeze_info {
+ __u32 pid;
+ __u32 enable;
+ __u32 timeout_ms;
+};
+
+struct binder_frozen_status_info {
+ __u32 pid;
+ __u32 sync_recv;
+ __u32 async_recv;
+};
+
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32)
@@ -274,6 +286,8 @@
#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
#define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref)
#define BINDER_SET_CONTEXT_MGR_EXT _IOW('b', 13, struct flat_binder_object)
+#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
+#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
/*
* NOTE: Two special error codes you should check for when calling
@@ -454,6 +468,12 @@
* The the last transaction (either a bcTRANSACTION or
* a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters.
*/
+
+ BR_FROZEN_REPLY = _IO('r', 18),
+ /*
+ * The target of the last transaction (either a bcTRANSACTION or
+ * a bcATTEMPT_ACQUIRE) is frozen. No parameters.
+ */
};
enum binder_driver_command_protocol {