xref: /openbsd-src/sys/dev/pci/drm/apple/systemep.c (revision 5dd0baa8251145fcf503b060e3a9291086883454)
1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
3 
4 #include <linux/completion.h>
5 
6 #include "afk.h"
7 #include "dcp.h"
8 #include "parser.h"
9 
10 static bool enable_verbose_logging;
11 module_param(enable_verbose_logging, bool, 0644);
12 MODULE_PARM_DESC(enable_verbose_logging, "Enable DCP firmware verbose logging");
13 
14 /*
15  * Serialized setProperty("gAFKConfigLogMask", 0xffff) IPC call which
16  * will set the DCP firmware log level to the most verbose setting
17  */
18 #define SYSTEM_SET_PROPERTY 0x43
19 static const u8 setprop_gAFKConfigLogMask_ffff[] = {
20 	0x14, 0x00, 0x00, 0x00, 0x67, 0x41, 0x46, 0x4b, 0x43, 0x6f,
21 	0x6e, 0x66, 0x69, 0x67, 0x4c, 0x6f, 0x67, 0x4d, 0x61, 0x73,
22 	0x6b, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0x40, 0x00,
23 	0x00, 0x84, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
24 };
25 
26 struct systemep_work {
27 	struct apple_epic_service *service;
28 	struct work_struct work;
29 };
30 
system_log_work(struct work_struct * work_)31 static void system_log_work(struct work_struct *work_)
32 {
33 	struct systemep_work *work =
34 		container_of(work_, struct systemep_work, work);
35 
36 	afk_send_command(work->service, SYSTEM_SET_PROPERTY,
37 			 setprop_gAFKConfigLogMask_ffff,
38 			 sizeof(setprop_gAFKConfigLogMask_ffff), NULL,
39 			 sizeof(setprop_gAFKConfigLogMask_ffff), NULL);
40 	complete(&work->service->ep->dcp->systemep_done);
41 	kfree(work);
42 }
43 
system_init(struct apple_epic_service * service,const char * name,const char * class,s64 unit)44 static void system_init(struct apple_epic_service *service, const char *name,
45 			const char *class, s64 unit)
46 {
47 	struct systemep_work *work;
48 
49 	if (!enable_verbose_logging)
50 		return;
51 
52 	/*
53 	 * We're called from the service message handler thread and can't
54 	 * dispatch blocking message from there.
55 	 */
56 	work = kzalloc(sizeof(*work), GFP_KERNEL);
57 	if (!work)
58 		return;
59 
60 	work->service = service;
61 	INIT_WORK(&work->work, system_log_work);
62 	schedule_work(&work->work);
63 }
64 
powerlog_init(struct apple_epic_service * service,const char * name,const char * class,s64 unit)65 static void powerlog_init(struct apple_epic_service *service, const char *name,
66 			  const char *class, s64 unit)
67 {
68 }
69 
powerlog_report(struct apple_epic_service * service,enum epic_subtype type,const void * data,size_t data_size)70 static int powerlog_report(struct apple_epic_service *service, enum epic_subtype type,
71 			 const void *data, size_t data_size)
72 {
73 	struct dcp_system_ev_mnits mnits;
74 	struct dcp_parse_ctx parse_ctx;
75 	struct apple_dcp *dcp = service->ep->dcp;
76 	int ret;
77 
78 	dev_dbg(dcp->dev, "systemep[ch:%u]: report type:%02x len:%zu\n",
79 		service->channel, type, data_size);
80 
81 	if (type != EPIC_SUBTYPE_STD_SERVICE)
82 		return 0;
83 
84 	ret = parse(data, data_size, &parse_ctx);
85 	if (ret) {
86 		dev_warn(service->ep->dcp->dev, "systemep: failed to parse report: %d\n", ret);
87 		return ret;
88 	}
89 
90 	ret = parse_system_log_mnits(&parse_ctx, &mnits);
91 	if (ret) {
92 		/* ignore parse errors in the case dcp sends unknown log events */
93 		dev_dbg(dcp->dev, "systemep: failed to parse mNits event: %d\n", ret);
94 		return 0;
95 	}
96 
97 	dev_dbg(dcp->dev, "systemep: mNits event: Nits: %u.%03u, iDAC: %u\n",
98 		mnits.millinits / 1000, mnits.millinits % 1000, mnits.idac);
99 
100 	dcp->brightness.nits = mnits.millinits / 1000;
101 
102 	return 0;
103 }
104 
105 static const struct apple_epic_service_ops systemep_ops[] = {
106 	{
107 		.name = "system",
108 		.init = system_init,
109 	},
110 	{
111 		.name = "powerlog-service",
112 		.init = powerlog_init,
113 		.report = powerlog_report,
114 	},
115 	{}
116 };
117 
systemep_init(struct apple_dcp * dcp)118 int systemep_init(struct apple_dcp *dcp)
119 {
120 	init_completion(&dcp->systemep_done);
121 
122 	dcp->systemep = afk_init(dcp, SYSTEM_ENDPOINT, systemep_ops);
123 	afk_start(dcp->systemep);
124 
125 	if (!enable_verbose_logging)
126 		return 0;
127 
128 	/*
129 	 * Timeouts aren't really fatal here: in the worst case we just weren't
130 	 * able to enable additional debug prints inside DCP
131 	 */
132 	if (!wait_for_completion_timeout(&dcp->systemep_done,
133 					 msecs_to_jiffies(MSEC_PER_SEC)))
134 		dev_err(dcp->dev, "systemep: couldn't enable verbose logs\n");
135 
136 	return 0;
137 }
138