sync with OpenBSD -current
This commit is contained in:
parent
f913a3fe74
commit
85b7ec3495
58 changed files with 10776 additions and 147 deletions
|
@ -1853,6 +1853,7 @@
|
|||
./usr/share/man/man3/CMS_get1_ReceiptRequest.3
|
||||
./usr/share/man/man3/CMS_sign.3
|
||||
./usr/share/man/man3/CMS_sign_receipt.3
|
||||
./usr/share/man/man3/CMS_signed_add1_attr.3
|
||||
./usr/share/man/man3/CMS_uncompress.3
|
||||
./usr/share/man/man3/CMS_verify.3
|
||||
./usr/share/man/man3/CMS_verify_receipt.3
|
||||
|
@ -3073,8 +3074,6 @@
|
|||
./usr/share/man/man3/wscanf.3
|
||||
./usr/share/man/man3/xdr.3
|
||||
./usr/share/man/man3/yp_bind.3
|
||||
./usr/share/man/man4/riscv64/stfpcie.4
|
||||
./usr/share/man/man4/riscv64/stfpciephy.4
|
||||
./usr/share/man/man9/KASSERT.9
|
||||
./usr/share/man/man9/RBT_INIT.9
|
||||
./usr/share/man/man9/SMR_LIST_INIT.9
|
||||
|
|
|
@ -1257,8 +1257,10 @@
|
|||
./usr/share/man/man4/arm64/aplaudio.4
|
||||
./usr/share/man/man4/arm64/aplcpu.4
|
||||
./usr/share/man/man4/arm64/apldart.4
|
||||
./usr/share/man/man4/arm64/apldcp.4
|
||||
./usr/share/man/man4/arm64/apldma.4
|
||||
./usr/share/man/man4/arm64/apldog.4
|
||||
./usr/share/man/man4/arm64/apldrm.4
|
||||
./usr/share/man/man4/arm64/aplefuse.4
|
||||
./usr/share/man/man4/arm64/aplhidev.4
|
||||
./usr/share/man/man4/arm64/apliic.4
|
||||
|
@ -1880,6 +1882,8 @@
|
|||
./usr/share/man/man4/riscv64/sfgpio.4
|
||||
./usr/share/man/man4/riscv64/sfuart.4
|
||||
./usr/share/man/man4/riscv64/stfclock.4
|
||||
./usr/share/man/man4/riscv64/stfpcie.4
|
||||
./usr/share/man/man4/riscv64/stfpciephy.4
|
||||
./usr/share/man/man4/riscv64/stfpinctrl.4
|
||||
./usr/share/man/man4/riscv64/stfrng.4
|
||||
./usr/share/man/man4/rkanxdp.4
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: cryptutil.c,v 1.12 2015/09/13 15:33:48 guenther Exp $ */
|
||||
/* $OpenBSD: cryptutil.c,v 1.13 2024/01/22 19:26:55 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
|
||||
*
|
||||
|
@ -54,7 +54,7 @@ int
|
|||
crypt_newhash(const char *pass, const char *pref, char *hash, size_t hashlen)
|
||||
{
|
||||
int rv = -1;
|
||||
const char *defaultpref = "blowfish,8";
|
||||
const char *defaultpref = "bcrypt,8";
|
||||
const char *errstr;
|
||||
const char *choices[] = { "blowfish", "bcrypt" };
|
||||
size_t maxchoice = sizeof(choices) / sizeof(choices[0]);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: devname.c,v 1.13 2016/07/06 04:35:12 guenther Exp $ */
|
||||
/* $OpenBSD: devname.c,v 1.14 2024/01/22 17:22:58 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
|
@ -83,7 +83,7 @@ devname(dev_t dev, mode_t type)
|
|||
char *name = NULL;
|
||||
|
||||
if (!db && !failure) {
|
||||
if (!(db = dbopen(_PATH_DEVDB, O_RDONLY, 0, DB_HASH, NULL)))
|
||||
if (!(db = __hash_open(_PATH_DEVDB, O_RDONLY, 0, NULL, 0)))
|
||||
failure = true;
|
||||
}
|
||||
if (!failure) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: getcap.c,v 1.36 2022/05/14 05:06:32 guenther Exp $ */
|
||||
/* $OpenBSD: getcap.c,v 1.37 2024/01/22 17:22:58 deraadt Exp $ */
|
||||
/*-
|
||||
* Copyright (c) 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
|
@ -252,7 +252,7 @@ getent(char **cap, u_int *len, char **db_array, FILE *fp,
|
|||
|
||||
clen = snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
|
||||
if (clen >= 0 && clen < sizeof(pbuf) && usedb &&
|
||||
(capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))) {
|
||||
(capdbp = __hash_open(pbuf, O_RDONLY, 0, NULL, 0))) {
|
||||
opened++;
|
||||
retval = cdbget(capdbp, &dbrecord, name);
|
||||
if (retval < 0) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: getnetgrent.c,v 1.31 2023/02/17 18:00:11 miod Exp $ */
|
||||
/* $OpenBSD: getnetgrent.c,v 1.32 2024/01/22 17:21:52 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1994 Christos Zoulas
|
||||
|
@ -630,7 +630,7 @@ setnetgrent(const char *ng)
|
|||
return;
|
||||
|
||||
if (_ng_db == NULL)
|
||||
_ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
|
||||
_ng_db = __hash_open(_PATH_NETGROUP_DB, O_RDONLY, 0, NULL, 0);
|
||||
|
||||
#ifdef YP
|
||||
/*
|
||||
|
@ -682,7 +682,7 @@ innetgr(const char *grp, const char *host, const char *user, const char *domain)
|
|||
struct stringlist *sl;
|
||||
|
||||
if (_ng_db == NULL)
|
||||
_ng_db = dbopen(_PATH_NETGROUP_DB, O_RDONLY, 0, DB_HASH, NULL);
|
||||
_ng_db = __hash_open(_PATH_NETGROUP_DB, O_RDONLY, 0, NULL, 0);
|
||||
|
||||
#ifdef YP
|
||||
/*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: getpwent.c,v 1.66 2022/08/02 17:00:15 deraadt Exp $ */
|
||||
/* $OpenBSD: getpwent.c,v 1.68 2024/01/22 21:07:09 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Theo de Raadt
|
||||
* Copyright (c) 1988, 1993
|
||||
|
@ -960,10 +960,20 @@ __initdb(int shadow)
|
|||
__ypmode = YPMODE_NONE;
|
||||
__getpwent_has_yppw = -1;
|
||||
#endif
|
||||
if (shadow)
|
||||
if (shadow) {
|
||||
#ifdef FORCE_DBOPEN
|
||||
_pw_db = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL);
|
||||
if (!_pw_db)
|
||||
#else
|
||||
_pw_db = __hash_open(_PATH_SMP_DB, O_RDONLY, 0, NULL, 0);
|
||||
#endif
|
||||
}
|
||||
if (!_pw_db) {
|
||||
#ifdef FORCE_DBOPEN
|
||||
_pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
|
||||
#else
|
||||
_pw_db = __hash_open(_PATH_MP_DB, O_RDONLY, 0, NULL, 0);
|
||||
#endif
|
||||
}
|
||||
if (_pw_db) {
|
||||
errno = saved_errno;
|
||||
return (1);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: ttyname.c,v 1.20 2017/04/14 15:02:51 deraadt Exp $ */
|
||||
/* $OpenBSD: ttyname.c,v 1.21 2024/01/22 17:22:58 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1988, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
|
@ -87,7 +87,7 @@ ttyname_r(int fd, char *buf, size_t len)
|
|||
|
||||
memcpy(buf, _PATH_DEV, sizeof(_PATH_DEV));
|
||||
|
||||
if ((db = dbopen(_PATH_DEVDB, O_RDONLY, 0, DB_HASH, NULL))) {
|
||||
if ((db = __hash_open(_PATH_DEVDB, O_RDONLY, 0, NULL, 0))) {
|
||||
memset(&bkey, 0, sizeof(bkey));
|
||||
bkey.type = S_IFCHR;
|
||||
bkey.dev = sb.st_rdev;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# $OpenBSD: Makefile.inc,v 1.18 2016/03/30 06:38:41 jmc Exp $
|
||||
# $OpenBSD: Makefile.inc,v 1.19 2024/01/22 16:18:06 deraadt Exp $
|
||||
|
||||
# librpc sources
|
||||
.PATH: ${LIBCSRCDIR}/arch/${MACHINE}/rpc ${LIBCSRCDIR}/rpc
|
||||
|
||||
SRCS+= auth_none.c auth_unix.c authunix_prot.c bindresvport.c \
|
||||
clnt_generic.c clnt_perror.c clnt_raw.c clnt_simple.c clnt_tcp.c \
|
||||
clnt_udp.c get_myaddress.c getrpcent.c getrpcport.c \
|
||||
clnt_udp.c clnt_udp_bufcreate.c get_myaddress.c getrpcent.c getrpcport.c \
|
||||
pmap_clnt.c pmap_getmaps.c pmap_getport.c pmap_prot.c \
|
||||
pmap_prot2.c pmap_rmt.c rpc_prot.c rpc_commondata.c rpc_callmsg.c \
|
||||
svc.c svc_auth.c svc_auth_unix.c svc_raw.c svc_run.c svc_simple.c \
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: clnt_udp.c,v 1.40 2022/08/24 01:32:21 deraadt Exp $ */
|
||||
/* $OpenBSD: clnt_udp.c,v 1.41 2024/01/22 16:18:06 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle America, Inc.
|
||||
|
@ -44,7 +44,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <rpc/pmap_clnt.h>
|
||||
#include "clnt_udp.h"
|
||||
|
||||
/*
|
||||
* UDP bases client side rpc operations
|
||||
|
@ -66,31 +66,65 @@ static const struct clnt_ops udp_ops = {
|
|||
clntudp_control
|
||||
};
|
||||
|
||||
/*
|
||||
* Private data kept per client handle
|
||||
*/
|
||||
struct cu_data {
|
||||
int cu_sock;
|
||||
bool_t cu_closeit;
|
||||
struct sockaddr_in cu_raddr;
|
||||
int cu_connected; /* use send() instead */
|
||||
int cu_rlen;
|
||||
struct timeval cu_wait;
|
||||
struct timeval cu_total;
|
||||
struct rpc_err cu_error;
|
||||
XDR cu_outxdrs;
|
||||
u_int cu_xdrpos;
|
||||
u_int cu_sendsz;
|
||||
char *cu_outbuf;
|
||||
u_int cu_recvsz;
|
||||
char cu_inbuf[1];
|
||||
};
|
||||
int
|
||||
clntudp_bufcreate1(struct clntudp_bufcreate_args *args)
|
||||
{
|
||||
args->cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
|
||||
if (args->cl == NULL) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
args->sendsz = ((args->sendsz + 3) / 4) * 4;
|
||||
args->recvsz = ((args->recvsz + 3) / 4) * 4;
|
||||
args->cu = (struct cu_data *)mem_alloc(sizeof(args->cu) +
|
||||
args->sendsz + args->recvsz);
|
||||
if (args->cu == NULL) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
args->cu->cu_outbuf = &args->cu->cu_inbuf[args->recvsz];
|
||||
args->cl->cl_ops = &udp_ops;
|
||||
args->cl->cl_private = (caddr_t)args->cu;
|
||||
args->cu->cu_connected = 0;
|
||||
args->cu->cu_rlen = sizeof (args->cu->cu_raddr);
|
||||
args->cu->cu_wait = args->wait;
|
||||
args->cu->cu_total.tv_sec = -1;
|
||||
args->cu->cu_total.tv_usec = -1;
|
||||
args->cu->cu_sendsz = args->sendsz;
|
||||
args->cu->cu_recvsz = args->recvsz;
|
||||
args->cu->cu_closeit = FALSE;
|
||||
args->call_msg.rm_xid = arc4random();
|
||||
args->call_msg.rm_direction = CALL;
|
||||
args->call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
|
||||
args->call_msg.rm_call.cb_prog = args->program;
|
||||
args->call_msg.rm_call.cb_vers = args->version;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
clntudp_bufcreate2(struct clntudp_bufcreate_args *args)
|
||||
{
|
||||
xdrmem_create(&(args->cu->cu_outxdrs), args->cu->cu_outbuf,
|
||||
args->sendsz, XDR_ENCODE);
|
||||
if (!xdr_callhdr(&(args->cu->cu_outxdrs), &args->call_msg))
|
||||
return -1;
|
||||
args->cu->cu_xdrpos = XDR_GETPOS(&(args->cu->cu_outxdrs));
|
||||
args->cl->cl_auth = authnone_create();
|
||||
if (args->cl->cl_auth == NULL) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a UDP based client handle.
|
||||
* If *sockp<0, *sockp is set to a newly created UPD socket.
|
||||
* If *sockp<0, *sockp is set to a newly created UPD socket. (***)
|
||||
* If raddr->sin_port is 0 a binder on the remote machine
|
||||
* is consulted for the correct port number.
|
||||
* is consulted for the correct port number. (***)
|
||||
* NB: It is the client's responsibility to close *sockp, unless
|
||||
* clntudp_bufcreate() was called with *sockp = -1 (so it created
|
||||
* the socket), and CLNT_DESTROY() is used.
|
||||
|
@ -103,100 +137,45 @@ struct cu_data {
|
|||
*
|
||||
* sendsz and recvsz are the maximum allowable packet sizes that can be
|
||||
* sent and received.
|
||||
*
|
||||
* This is a reduced-functionality version of clntudp_bufcreate() that
|
||||
* does not allocate socket or binding (***, above).
|
||||
* The official function clntudp_bufcreate(), which does perform those
|
||||
* two steps, is in clnt_udp_bufcreate.c. This split avoids pulling
|
||||
* socket / portmap related code into programs only using getpwent / YP code.
|
||||
*/
|
||||
|
||||
CLIENT *
|
||||
clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
clntudp_bufcreate_simple(struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
struct timeval wait, int *sockp, u_int sendsz, u_int recvsz)
|
||||
{
|
||||
CLIENT *cl;
|
||||
struct cu_data *cu = NULL;
|
||||
struct rpc_msg call_msg;
|
||||
struct clntudp_bufcreate_args args;
|
||||
|
||||
cl = (CLIENT *)mem_alloc(sizeof(CLIENT));
|
||||
if (cl == NULL) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
goto fooy;
|
||||
}
|
||||
sendsz = ((sendsz + 3) / 4) * 4;
|
||||
recvsz = ((recvsz + 3) / 4) * 4;
|
||||
cu = (struct cu_data *)mem_alloc(sizeof(*cu) + sendsz + recvsz);
|
||||
if (cu == NULL) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
goto fooy;
|
||||
}
|
||||
cu->cu_outbuf = &cu->cu_inbuf[recvsz];
|
||||
args.raddr = raddr;
|
||||
args.program = program;
|
||||
args.version = version;
|
||||
args.wait = wait;
|
||||
args.sockp = sockp;
|
||||
args.sendsz = sendsz;
|
||||
args.recvsz = recvsz;
|
||||
args.cl = NULL;
|
||||
args.cu = NULL;
|
||||
|
||||
if (raddr->sin_port == 0) {
|
||||
u_short port;
|
||||
if ((port =
|
||||
pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
|
||||
goto fooy;
|
||||
}
|
||||
raddr->sin_port = htons(port);
|
||||
}
|
||||
cl->cl_ops = &udp_ops;
|
||||
cl->cl_private = (caddr_t)cu;
|
||||
cu->cu_raddr = *raddr;
|
||||
cu->cu_connected = 0;
|
||||
cu->cu_rlen = sizeof (cu->cu_raddr);
|
||||
cu->cu_wait = wait;
|
||||
cu->cu_total.tv_sec = -1;
|
||||
cu->cu_total.tv_usec = -1;
|
||||
cu->cu_sendsz = sendsz;
|
||||
cu->cu_recvsz = recvsz;
|
||||
call_msg.rm_xid = arc4random();
|
||||
call_msg.rm_direction = CALL;
|
||||
call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
|
||||
call_msg.rm_call.cb_prog = program;
|
||||
call_msg.rm_call.cb_vers = version;
|
||||
xdrmem_create(&(cu->cu_outxdrs), cu->cu_outbuf,
|
||||
sendsz, XDR_ENCODE);
|
||||
if (!xdr_callhdr(&(cu->cu_outxdrs), &call_msg)) {
|
||||
if (clntudp_bufcreate1(&args) == -1)
|
||||
goto fooy;
|
||||
}
|
||||
cu->cu_xdrpos = XDR_GETPOS(&(cu->cu_outxdrs));
|
||||
if (*sockp < 0) {
|
||||
*sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK,
|
||||
IPPROTO_UDP);
|
||||
if (*sockp == -1) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
goto fooy;
|
||||
}
|
||||
/* attempt to bind to priv port */
|
||||
(void)bindresvport(*sockp, NULL);
|
||||
cu->cu_closeit = TRUE;
|
||||
} else {
|
||||
cu->cu_closeit = FALSE;
|
||||
}
|
||||
cu->cu_sock = *sockp;
|
||||
cl->cl_auth = authnone_create();
|
||||
if (cl->cl_auth == NULL) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
args.cu->cu_raddr = *raddr;
|
||||
args.cu->cu_sock = *sockp;
|
||||
if (clntudp_bufcreate2(&args) == -1)
|
||||
goto fooy;
|
||||
}
|
||||
return (cl);
|
||||
return (args.cl);
|
||||
fooy:
|
||||
if (cu)
|
||||
mem_free((caddr_t)cu, sizeof(*cu) + sendsz + recvsz);
|
||||
if (cl)
|
||||
mem_free((caddr_t)cl, sizeof(CLIENT));
|
||||
if (args.cu)
|
||||
mem_free((caddr_t)args.cu,
|
||||
sizeof(*args.cu) + args.sendsz + args.recvsz);
|
||||
if (args.cl)
|
||||
mem_free((caddr_t)args.cl, sizeof(CLIENT));
|
||||
return (NULL);
|
||||
}
|
||||
DEF_WEAK(clntudp_bufcreate);
|
||||
|
||||
CLIENT *
|
||||
clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
struct timeval wait, int *sockp)
|
||||
{
|
||||
|
||||
return(clntudp_bufcreate(raddr, program, version, wait, sockp,
|
||||
UDPMSGSIZE, UDPMSGSIZE));
|
||||
}
|
||||
DEF_WEAK(clntudp_create);
|
||||
|
||||
static enum clnt_stat
|
||||
clntudp_call(CLIENT *cl, /* client handle */
|
||||
|
|
70
lib/libc/rpc/clnt_udp.h
Normal file
70
lib/libc/rpc/clnt_udp.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/* $OpenBSD: clnt_udp.h,v 1.1 2024/01/22 16:18:06 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle America, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the "Oracle America, Inc." nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Private data kept per client handle
|
||||
*/
|
||||
struct cu_data {
|
||||
int cu_sock;
|
||||
bool_t cu_closeit;
|
||||
struct sockaddr_in cu_raddr;
|
||||
int cu_connected; /* use send() instead */
|
||||
int cu_rlen;
|
||||
struct timeval cu_wait;
|
||||
struct timeval cu_total;
|
||||
struct rpc_err cu_error;
|
||||
XDR cu_outxdrs;
|
||||
u_int cu_xdrpos;
|
||||
u_int cu_sendsz;
|
||||
char *cu_outbuf;
|
||||
u_int cu_recvsz;
|
||||
char cu_inbuf[1];
|
||||
};
|
||||
|
||||
struct clntudp_bufcreate_args {
|
||||
struct sockaddr_in *raddr;
|
||||
u_long program;
|
||||
u_long version;
|
||||
struct timeval wait;
|
||||
int *sockp;
|
||||
u_int sendsz;
|
||||
u_int recvsz;
|
||||
CLIENT *cl;
|
||||
struct cu_data *cu;
|
||||
struct rpc_msg call_msg;
|
||||
};
|
||||
|
||||
__BEGIN_HIDDEN_DECLS
|
||||
extern int clntudp_bufcreate1(struct clntudp_bufcreate_args *);
|
||||
extern int clntudp_bufcreate2(struct clntudp_bufcreate_args *);
|
||||
__END_HIDDEN_DECLS
|
130
lib/libc/rpc/clnt_udp_bufcreate.c
Normal file
130
lib/libc/rpc/clnt_udp_bufcreate.c
Normal file
|
@ -0,0 +1,130 @@
|
|||
/* $OpenBSD: clnt_udp_bufcreate.c,v 1.1 2024/01/22 16:18:06 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010, Oracle America, Inc.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
* * Neither the name of the "Oracle America, Inc." nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* clnt_udp.c, Implements a UDP/IP based, client side RPC.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <rpc/rpc.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <rpc/pmap_clnt.h>
|
||||
#include "clnt_udp.h"
|
||||
|
||||
/*
|
||||
* Create a UDP based client handle.
|
||||
* If *sockp<0, *sockp is set to a newly created UPD socket.
|
||||
* If raddr->sin_port is 0 a binder on the remote machine
|
||||
* is consulted for the correct port number.
|
||||
* NB: It is the client's responsibility to close *sockp, unless
|
||||
* clntudp_bufcreate() was called with *sockp = -1 (so it created
|
||||
* the socket), and CLNT_DESTROY() is used.
|
||||
* NB: The rpch->cl_auth is initialized to null authentication.
|
||||
* Caller may wish to set this something more useful.
|
||||
*
|
||||
* wait is the amount of time used between retransmitting a call if
|
||||
* no response has been heard; retransmission occurs until the actual
|
||||
* rpc call times out.
|
||||
*
|
||||
* sendsz and recvsz are the maximum allowable packet sizes that can be
|
||||
* sent and received.
|
||||
*/
|
||||
|
||||
CLIENT *
|
||||
clntudp_bufcreate(struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
struct timeval wait, int *sockp, u_int sendsz, u_int recvsz)
|
||||
{
|
||||
struct clntudp_bufcreate_args args;
|
||||
|
||||
args.raddr = raddr;
|
||||
args.program = program;
|
||||
args.version = version;
|
||||
args.wait = wait;
|
||||
args.sockp = sockp;
|
||||
args.sendsz = sendsz;
|
||||
args.recvsz = recvsz;
|
||||
|
||||
if (clntudp_bufcreate1(&args) == -1)
|
||||
goto fooy;
|
||||
|
||||
if (raddr->sin_port == 0) {
|
||||
u_short port;
|
||||
if ((port =
|
||||
pmap_getport(raddr, program, version, IPPROTO_UDP)) == 0) {
|
||||
goto fooy;
|
||||
}
|
||||
raddr->sin_port = htons(port);
|
||||
}
|
||||
args.cu->cu_raddr = *raddr;
|
||||
if (*sockp < 0) {
|
||||
*sockp = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK,
|
||||
IPPROTO_UDP);
|
||||
if (*sockp == -1) {
|
||||
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
|
||||
rpc_createerr.cf_error.re_errno = errno;
|
||||
goto fooy;
|
||||
}
|
||||
/* attempt to bind to priv port */
|
||||
(void)bindresvport(*sockp, NULL);
|
||||
args.cu->cu_closeit = TRUE;
|
||||
}
|
||||
args.cu->cu_sock = *args.sockp;
|
||||
|
||||
if (clntudp_bufcreate2(&args) == -1)
|
||||
goto fooy;
|
||||
return (args.cl);
|
||||
fooy:
|
||||
if (args.cu)
|
||||
mem_free((caddr_t)args.cu,
|
||||
sizeof(*args.cu) + args.sendsz + args.recvsz);
|
||||
if (args.cl)
|
||||
mem_free((caddr_t)args.cl, sizeof(CLIENT));
|
||||
return (NULL);
|
||||
}
|
||||
DEF_WEAK(clntudp_bufcreate);
|
||||
|
||||
CLIENT *
|
||||
clntudp_create(struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
struct timeval wait, int *sockp)
|
||||
{
|
||||
|
||||
return(clntudp_bufcreate(raddr, program, version, wait, sockp,
|
||||
UDPMSGSIZE, UDPMSGSIZE));
|
||||
}
|
||||
DEF_WEAK(clntudp_create);
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: yp_bind.c,v 1.32 2022/08/02 16:59:29 deraadt Exp $ */
|
||||
/* $OpenBSD: yp_bind.c,v 1.33 2024/01/22 16:18:06 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1992, 1993, 1996 Theo de Raadt <deraadt@theos.com>
|
||||
* All rights reserved.
|
||||
|
@ -46,6 +46,10 @@
|
|||
char _yp_domain[HOST_NAME_MAX+1];
|
||||
int _yplib_timeout = 10;
|
||||
|
||||
extern CLIENT *
|
||||
clntudp_bufcreate_simple(struct sockaddr_in *raddr, u_long program, u_long version,
|
||||
struct timeval wait, int *sockp, u_int sendsz, u_int recvsz);
|
||||
|
||||
int
|
||||
_yp_dobind(const char *dom, struct dom_binding **ypdb)
|
||||
{
|
||||
|
@ -72,8 +76,8 @@ again:
|
|||
|
||||
tv.tv_sec = _yplib_timeout / 2;
|
||||
tv.tv_usec = 0;
|
||||
ypbinding->dom_client = clntudp_create(&ypbinding->dom_server_addr,
|
||||
YPPROG, YPVERS, tv, &ypbinding->dom_socket);
|
||||
ypbinding->dom_client = clntudp_bufcreate_simple(&ypbinding->dom_server_addr,
|
||||
YPPROG, YPVERS, tv, &ypbinding->dom_socket, UDPMSGSIZE, UDPMSGSIZE);
|
||||
if (ypbinding->dom_client == NULL) {
|
||||
close(ypbinding->dom_socket);
|
||||
ypbinding->dom_socket = -1;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.\" $OpenBSD: CMS_ContentInfo_new.3,v 1.3 2019/11/02 15:39:46 schwarze Exp $
|
||||
.\" $OpenBSD: CMS_ContentInfo_new.3,v 1.4 2024/01/22 14:00:13 tb Exp $
|
||||
.\" Copyright (c) 2019 Ingo Schwarze <schwarze@openbsd.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
|
@ -13,7 +13,7 @@
|
|||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: November 2 2019 $
|
||||
.Dd $Mdocdate: January 22 2024 $
|
||||
.Dt CMS_CONTENTINFO_NEW 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -104,6 +104,7 @@ if an error occurs.
|
|||
.Xr CMS_get1_ReceiptRequest 3 ,
|
||||
.Xr CMS_sign 3 ,
|
||||
.Xr CMS_sign_receipt 3 ,
|
||||
.Xr CMS_signed_add1_attr 3 ,
|
||||
.Xr CMS_uncompress 3 ,
|
||||
.Xr CMS_verify 3 ,
|
||||
.Xr CMS_verify_receipt 3 ,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.\" $OpenBSD: CMS_get0_SignerInfos.3,v 1.8 2023/07/26 19:30:43 tb Exp $
|
||||
.\" $OpenBSD: CMS_get0_SignerInfos.3,v 1.9 2024/01/22 14:00:13 tb Exp $
|
||||
.\" full merge up to: OpenSSL 83cf7abf May 29 13:07:08 2018 +0100
|
||||
.\"
|
||||
.\" This file was written by Dr. Stephen Henson <steve@openssl.org>.
|
||||
|
@ -48,7 +48,7 @@
|
|||
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
.\" OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd $Mdocdate: July 26 2023 $
|
||||
.Dd $Mdocdate: January 22 2024 $
|
||||
.Dt CMS_GET0_SIGNERINFOS 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -187,6 +187,7 @@ Any error can be obtained from
|
|||
.Xr ERR_get_error 3 .
|
||||
.Sh SEE ALSO
|
||||
.Xr CMS_ContentInfo_new 3 ,
|
||||
.Xr CMS_signed_add1_attr 3 ,
|
||||
.Xr CMS_verify 3
|
||||
.Sh STANDARDS
|
||||
RFC 5652: Cryptographic Message Syntax (CMS)
|
||||
|
|
371
lib/libcrypto/man/CMS_signed_add1_attr.3
Normal file
371
lib/libcrypto/man/CMS_signed_add1_attr.3
Normal file
|
@ -0,0 +1,371 @@
|
|||
.\" $OpenBSD: CMS_signed_add1_attr.3,v 1.3 2024/01/22 14:00:13 tb Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2024 Job Snijders <job@openbsd.org>
|
||||
.\" Copyright (c) 2024 Theo Buehler <tb@openbsd.org>
|
||||
.\" Copyright (c) 2021 Ingo Schwarze <schwarze@openbsd.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: January 22 2024 $
|
||||
.Dt CMS_SIGNED_ADD1_ATTR 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm CMS_signed_add1_attr ,
|
||||
.Nm CMS_signed_add1_attr_by_NID ,
|
||||
.Nm CMS_signed_add1_attr_by_OBJ ,
|
||||
.Nm CMS_signed_add1_attr_by_txt ,
|
||||
.Nm CMS_signed_delete_attr ,
|
||||
.Nm CMS_signed_get0_data_by_OBJ ,
|
||||
.Nm CMS_signed_get_attr ,
|
||||
.Nm CMS_signed_get_attr_by_NID ,
|
||||
.Nm CMS_signed_get_attr_by_OBJ ,
|
||||
.Nm CMS_signed_get_attr_count ,
|
||||
.Nm CMS_unsigned_add1_attr ,
|
||||
.Nm CMS_unsigned_add1_attr_by_NID ,
|
||||
.Nm CMS_unsigned_add1_attr_by_OBJ ,
|
||||
.Nm CMS_unsigned_add1_attr_by_txt ,
|
||||
.Nm CMS_unsigned_delete_attr ,
|
||||
.Nm CMS_unsigned_get0_data_by_OBJ ,
|
||||
.Nm CMS_unsigned_get_attr ,
|
||||
.Nm CMS_unsigned_get_attr_by_NID ,
|
||||
.Nm CMS_unsigned_get_attr_by_OBJ ,
|
||||
.Nm CMS_unsigned_get_attr_count
|
||||
.Nd change signed and unsigned attributes of a CMS SignerInfo object
|
||||
.Sh SYNOPSIS
|
||||
.In openssl/cms.h
|
||||
.Ft int
|
||||
.Fo CMS_signed_add1_attr
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "X509_ATTRIBUTE *attr"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_signed_add1_attr_by_NID
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "int nid"
|
||||
.Fa "int type"
|
||||
.Fa "const void *bytes"
|
||||
.Fa "int len"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_signed_add1_attr_by_OBJ
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "const ASN1_OBJECT *obj"
|
||||
.Fa "int type"
|
||||
.Fa "const void *bytes"
|
||||
.Fa "int len"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_signed_add1_attr_by_txt
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "const char *attrname"
|
||||
.Fa "int type"
|
||||
.Fa "const void *bytes"
|
||||
.Fa "int len"
|
||||
.Fc
|
||||
.Ft "X509_ATTRIBUTE *"
|
||||
.Fo CMS_signed_delete_attr
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "int loc"
|
||||
.Fc
|
||||
.Ft "void *"
|
||||
.Fo CMS_signed_get0_data_by_OBJ
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "const ASN1_OBJECT *oid"
|
||||
.Fa "int lastpos"
|
||||
.Fa "int type"
|
||||
.Fc
|
||||
.Ft "X509_ATTRIBUTE *"
|
||||
.Fo CMS_signed_get_attr
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fa "int loc"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_signed_get_attr_by_NID
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fa "int nid"
|
||||
.Fa "int lastpos"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_signed_get_attr_by_OBJ
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fa "const ASN1_OBJECT *obj"
|
||||
.Fa "int lastpos"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_signed_get_attr_count
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_unsigned_add1_attr
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "X509_ATTRIBUTE *attr"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_unsigned_add1_attr_by_NID
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "int nid"
|
||||
.Fa "int type"
|
||||
.Fa "const void *bytes"
|
||||
.Fa "int len"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_unsigned_add1_attr_by_OBJ
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "const ASN1_OBJECT *obj"
|
||||
.Fa "int type"
|
||||
.Fa "const void *bytes"
|
||||
.Fa "int len"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_unsigned_add1_attr_by_txt
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "const char *attrname"
|
||||
.Fa "int type"
|
||||
.Fa "const void *bytes"
|
||||
.Fa "int len"
|
||||
.Fc
|
||||
.Ft "X509_ATTRIBUTE *"
|
||||
.Fo CMS_unsigned_delete_attr
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "int loc"
|
||||
.Fc
|
||||
.Ft "void *"
|
||||
.Fo CMS_unsigned_get0_data_by_OBJ
|
||||
.Fa "CMS_SignerInfo *si"
|
||||
.Fa "ASN1_OBJECT *oid"
|
||||
.Fa "int lastpos"
|
||||
.Fa "int type"
|
||||
.Fc
|
||||
.Ft "X509_ATTRIBUTE *"
|
||||
.Fo CMS_unsigned_get_attr
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fa "int loc"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_unsigned_get_attr_by_NID
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fa "int nid"
|
||||
.Fa "int lastpos"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_unsigned_get_attr_by_OBJ
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fa "const ASN1_OBJECT *obj"
|
||||
.Fa "int lastpos"
|
||||
.Fc
|
||||
.Ft int
|
||||
.Fo CMS_unsigned_get_attr_count
|
||||
.Fa "const CMS_SignerInfo *si"
|
||||
.Fc
|
||||
.Sh DESCRIPTION
|
||||
A
|
||||
.Em CMS_SignerInfo
|
||||
object has two optional sets of X.501 attributes:
|
||||
a set of signed attributes in the
|
||||
.Fa signedAttrs
|
||||
array and a set of unsigned attributes in the
|
||||
.Fa unsignedAttrs
|
||||
array.
|
||||
The functions in this manual are wrappers of the
|
||||
.Fn X509at_*
|
||||
functions.
|
||||
All arguments except
|
||||
.Fa si
|
||||
are passed to
|
||||
.Fn X509at_* .
|
||||
The
|
||||
.Fn CMS_signed_*
|
||||
and
|
||||
.Fn CMS_unsigned_*
|
||||
functions are similar, except
|
||||
.Fn CMS_signed_*
|
||||
calls
|
||||
.Fn X509at_*
|
||||
with the
|
||||
.Em CMS_SignerInfo
|
||||
object's set of signed attributes and
|
||||
.Fn CMS_unsigned_*
|
||||
calls
|
||||
.Fn X509at_*
|
||||
with the
|
||||
.Em CMS_SignerInfo
|
||||
object's set of unsigned attributes.
|
||||
For brevity only the
|
||||
.Fn CMS_signed_*
|
||||
functions are described below.
|
||||
.Pp
|
||||
.Fn CMS_signed_add1_attr
|
||||
appends a deep copy of
|
||||
.Fa attr
|
||||
to the
|
||||
.Fa signedAttrs
|
||||
array of
|
||||
.Fa si ,
|
||||
allocating a new array if necessary.
|
||||
.Pp
|
||||
.Fn CMS_signed_add1_attr_by_NID ,
|
||||
.Fn CMS_signed_add1_attr_by_OBJ ,
|
||||
and
|
||||
.Fn CMS_signed_add1_attr_by_txt
|
||||
create a new X.501 Attribute object using
|
||||
.Xr X509at_add1_attr_by_NID 3 ,
|
||||
.Xr X509at_add1_attr_by_OBJ 3 ,
|
||||
and
|
||||
.Xr X509at_add1_attr_by_txt 3 ,
|
||||
and append it to the
|
||||
.Fa signedAttrs
|
||||
array of
|
||||
.Fa si .
|
||||
.Pp
|
||||
.Fn CMS_signed_delete_attr
|
||||
deletes the element with the zero-based
|
||||
.Fa loc
|
||||
in
|
||||
.Fa signedAttrs
|
||||
of
|
||||
.Fa si .
|
||||
.Pp
|
||||
.Fn CMS_signed_get0_data_by_OBJ ,
|
||||
.Fn CMS_signed_get_attr_by_NID ,
|
||||
and
|
||||
.Fn CMS_signed_get_attr_by_OBJ
|
||||
search the array starting after the index
|
||||
.Fa lastpos .
|
||||
They fail if no matching object is found.
|
||||
.Fn CMS_signed_get0_data_by_OBJ
|
||||
also fails if the data is not of the requested
|
||||
.Fa type .
|
||||
.Pp
|
||||
Additionally, the
|
||||
.Fa lastpos
|
||||
argument of
|
||||
.Fn CMS_signed_get0_data_by_OBJ
|
||||
is interpreted in a special way.
|
||||
If
|
||||
.Fa lastpos
|
||||
is \-2 or smaller, the function also fails if the
|
||||
.Fa signedAttrs
|
||||
array of
|
||||
.Fa si ,
|
||||
contains more than one matching object.
|
||||
If
|
||||
.Fa lastpos
|
||||
is \-3 or smaller, it also fails unless the matching object contains exactly
|
||||
one value.
|
||||
.Pp
|
||||
.Fn CMS_signed_get_attr
|
||||
returns the array element at the zero-based
|
||||
.Fa loc .
|
||||
It fails if the
|
||||
.Fa loc
|
||||
argument is negative or greater than or equal to the number of objects in the
|
||||
array.
|
||||
.Pp
|
||||
.Fn CMS_signed_get_attr_count
|
||||
returns the number of objects currently stored in the
|
||||
.Fa signedAttrs
|
||||
array of
|
||||
.Fa si .
|
||||
.Sh RETURN VALUES
|
||||
.Fn CMS_signed_add1_attr ,
|
||||
.Fn CMS_signed_add1_attr_by_NID ,
|
||||
.Fn CMS_signed_add1_attr_by_OBJ ,
|
||||
.Fn CMS_signed_add1_attr_by_txt ,
|
||||
.Fn CMS_unsigned_add1_attr ,
|
||||
.Fn CMS_unsigned_add1_attr_by_NID ,
|
||||
.Fn CMS_unsigned_add1_attr_by_OBJ ,
|
||||
and
|
||||
.Fn CMS_unsigned_add1_attr_by_txt
|
||||
return 1 for success or 0 if an error occurs.
|
||||
.Pp
|
||||
.Fn CMS_signed_delete_attr
|
||||
returns the deleted element or
|
||||
.Dv NULL
|
||||
if the
|
||||
.Fa signedAttrs
|
||||
array is
|
||||
.Dv NULL ,
|
||||
or if the requested
|
||||
.Fa loc
|
||||
argument is negative, or greater than or equal to the number of objects in it.
|
||||
.Pp
|
||||
.Fn CMS_unsigned_delete_attr
|
||||
returns the deleted element or
|
||||
.Dv NULL
|
||||
if the
|
||||
.Fa unsignedAttrs
|
||||
array is
|
||||
.Dv NULL ,
|
||||
or if the requested
|
||||
.Fa loc
|
||||
argument is negative, or greater than or equal to the number of objects in it.
|
||||
.Pp
|
||||
.Fn CMS_signed_get0_data_by_OBJ
|
||||
and
|
||||
.Fn CMS_unsigned_get0_data_by_OBJ
|
||||
return an internal pointer to the data contained in the value of the first
|
||||
object that has an index greater than
|
||||
.Fa lastpos
|
||||
and a type matching
|
||||
.Fa type ,
|
||||
or NULL on failure.
|
||||
.Pp
|
||||
.Fn CMS_signed_get_attr
|
||||
and
|
||||
.Fn CMS_unsigned_get_attr
|
||||
return an internal pointer or NULL on failure.
|
||||
.Pp
|
||||
.Fn CMS_signed_get_attr_by_NID ,
|
||||
.Fn CMS_signed_get_attr_by_OBJ ,
|
||||
.Fn CMS_unsigned_get_attr_by_NID ,
|
||||
and
|
||||
.Fn CMS_unsigned_get_attr_by_OBJ
|
||||
return the index of the first object in the array that has an index greater than
|
||||
.Fa lastpos
|
||||
and a type matching
|
||||
.Fa nid
|
||||
or
|
||||
.Fa oid ,
|
||||
respectively, or \-1 on failure.
|
||||
In addition,
|
||||
.Fn CMS_signed_get_attr_by_OBJ
|
||||
and
|
||||
.Fn CMS_unsigned_get_attr_by_OBJ
|
||||
return \-2 if
|
||||
.Xr OBJ_nid2obj 3
|
||||
fails on the requested
|
||||
.Fa nid .
|
||||
.Pp
|
||||
.Fn CMS_signed_get_attr_count
|
||||
and
|
||||
.Fn CMS_unsigned_get_attr_count
|
||||
return the number of array elements or \-1 on failure.
|
||||
.Sh SEE ALSO
|
||||
.Xr CMS_add1_signer 3 ,
|
||||
.Xr CMS_ContentInfo_new 3 ,
|
||||
.Xr CMS_get0_SignerInfos 3 ,
|
||||
.Xr OBJ_nid2obj 3 ,
|
||||
.Xr X509_ATTRIBUTE_create_by_OBJ 3 ,
|
||||
.Xr X509_ATTRIBUTE_new 3 ,
|
||||
.Xr X509at_add1_attr 3
|
||||
.Sh STANDARDS
|
||||
RFC 5652: Cryptographic Message Syntax (CMS)
|
||||
.Bl -dash -compact -offset indent
|
||||
.It
|
||||
section 5.3: SignerInfo Type
|
||||
.It
|
||||
section 11: Useful Attributes
|
||||
.El
|
||||
.Sh HISTORY
|
||||
These functions first appeared in OpenSSL 0.9.9 and have been available since
|
||||
.Ox 6.6 .
|
|
@ -1,4 +1,4 @@
|
|||
# $OpenBSD: Makefile,v 1.280 2023/12/29 19:15:15 tb Exp $
|
||||
# $OpenBSD: Makefile,v 1.281 2024/01/22 13:44:59 job Exp $
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
|
@ -100,6 +100,7 @@ MAN= \
|
|||
CMS_get1_ReceiptRequest.3 \
|
||||
CMS_sign.3 \
|
||||
CMS_sign_receipt.3 \
|
||||
CMS_signed_add1_attr.3 \
|
||||
CMS_uncompress.3 \
|
||||
CMS_verify.3 \
|
||||
CMS_verify_receipt.3 \
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# $OpenBSD: Makefile,v 1.33 2023/12/11 11:17:34 claudio Exp $
|
||||
# $OpenBSD: Makefile,v 1.34 2024/01/22 18:56:18 kettenis Exp $
|
||||
|
||||
MAN= agintc.4 agtimer.4 ampchwm.4 ampintc.4 \
|
||||
aplaudio.4 aplcpu.4 apldart.4 apldma.4 apldog.4 aplefuse.4 \
|
||||
aplaudio.4 aplcpu.4 apldart.4 apldcp.4 apldma.4 apldog.4 apldrm.4 \
|
||||
aplefuse.4 \
|
||||
aplhidev.4 apliic.4 aplintc.4 aplmbox.4 aplmca.4 aplnco.4 aplns.4 \
|
||||
aplpcie.4 aplpinctrl.4 aplpmgr.4 aplpmu.4 aplpwm.4 \
|
||||
aplsart.4 aplsmc.4 aplspi.4 aplspmi.4 apm.4 \
|
||||
|
|
49
share/man/man4/man4.arm64/apldcp.4
Normal file
49
share/man/man4/man4.arm64/apldcp.4
Normal file
|
@ -0,0 +1,49 @@
|
|||
.\" $OpenBSD: apldcp.4,v 1.1 2024/01/22 18:56:18 kettenis Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: January 22 2024 $
|
||||
.Dt APLDCP 4 arm64
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm apldcp
|
||||
.Nd Apple display coprocessor
|
||||
.Sh SYNOPSIS
|
||||
.Cd "apldcp* at fdt?"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver controls the display coprocessor integrated on Apple SoCs.
|
||||
.Sh SEE ALSO
|
||||
.Xr apldrm 4 ,
|
||||
.Xr drm 4 ,
|
||||
.Xr intro 4
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
device driver first appeared in
|
||||
.Ox 7.5 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
driver was written by
|
||||
.An Alyssa Rosenzweig
|
||||
and
|
||||
.An Janne Grunau
|
||||
for Linux and ported to
|
||||
.Ox
|
||||
by
|
||||
.An Mark Kettenis Aq Mt kettenis@openbsd.org .
|
52
share/man/man4/man4.arm64/apldrm.4
Normal file
52
share/man/man4/man4.arm64/apldrm.4
Normal file
|
@ -0,0 +1,52 @@
|
|||
.\" $OpenBSD: apldrm.4,v 1.1 2024/01/22 18:56:18 kettenis Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2024 Mark Kettenis <kettenis@openbsd.org>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: January 22 2024 $
|
||||
.Dt APLDRM 4 arm64
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm apldrm
|
||||
.Nd Apple DRM subsystem
|
||||
.Sh SYNOPSIS
|
||||
.Cd "aplrm* at fdt?"
|
||||
.Cd "drm* at apldrm?"
|
||||
.Cd "wsdisplay* at apldrm?"
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver provides kernel mode setting (KMS) functionality for the
|
||||
graphics hardware integrated on Apple SoCs.
|
||||
.Sh SEE ALSO
|
||||
.Xr apldcp 4 ,
|
||||
.Xr drm 4 ,
|
||||
.Xr intro 4
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
device driver first appeared in
|
||||
.Ox 7.5 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
driver was written by
|
||||
.An Alyssa Rosenzweig
|
||||
and
|
||||
.An Janne Grunau
|
||||
for Linux and ported to
|
||||
.Ox
|
||||
by
|
||||
.An Mark Kettenis Aq Mt kettenis@openbsd.org .
|
|
@ -30,10 +30,10 @@
|
|||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $OpenBSD: login.conf.5,v 1.71 2023/12/29 11:57:38 claudio Exp $
|
||||
.\" $OpenBSD: login.conf.5,v 1.72 2024/01/22 19:26:55 deraadt Exp $
|
||||
.\" BSDI $From: login.conf.5,v 2.20 2000/06/26 14:50:38 prb Exp $
|
||||
.\"
|
||||
.Dd $Mdocdate: December 29 2023 $
|
||||
.Dd $Mdocdate: January 22 2024 $
|
||||
.Dt LOGIN.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -98,7 +98,7 @@ file are either boolean or use a
|
|||
.Ql =
|
||||
to separate the capability from the value.
|
||||
The types are described after the capability table.
|
||||
.Bl -column "approve-service" "program" "blowfish,8" "Description"
|
||||
.Bl -column "approve-service" "program" "bcrypt,8" "Description"
|
||||
.It Sy Name Ta Sy Type Ta Sy Default Ta Sy Description
|
||||
.\"
|
||||
.It approve Ta program Ta "" Ta
|
||||
|
@ -165,7 +165,7 @@ See
|
|||
.Xr login 1 .
|
||||
.\"
|
||||
.Pp
|
||||
.It localcipher Ta string Ta blowfish,a Ta
|
||||
.It localcipher Ta string Ta bcrypt,a Ta
|
||||
The cipher to use for encrypting passwords.
|
||||
Refer to
|
||||
.Xr crypt_newhash 3
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $OpenBSD: GENERIC,v 1.280 2024/01/19 06:59:10 mlarkin Exp $
|
||||
# $OpenBSD: GENERIC,v 1.281 2024/01/22 18:54:01 kettenis Exp $
|
||||
#
|
||||
# GENERIC machine description file
|
||||
#
|
||||
|
@ -164,8 +164,12 @@ apldckbd* at apldchidev?
|
|||
wskbd* at apldckbd? mux 1
|
||||
apldcms* at apldchidev?
|
||||
wsmouse* at apldcms? mux 0
|
||||
apldcp* at fdt?
|
||||
apldma* at fdt?
|
||||
apldog* at fdt? early 1
|
||||
apldrm* at fdt?
|
||||
drm* at apldrm?
|
||||
wsdisplay* at apldrm?
|
||||
aplefuse* at fdt? early 1
|
||||
apliic* at fdt?
|
||||
iic* at apliic?
|
||||
|
|
989
sys/dev/pci/drm/apple/afk.c
Normal file
989
sys/dev/pci/drm/apple/afk.c
Normal file
|
@ -0,0 +1,989 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/soc/apple/rtkit.h>
|
||||
|
||||
#include "afk.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct afk_receive_message_work {
|
||||
struct apple_dcp_afkep *ep;
|
||||
u64 message;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
#define RBEP_TYPE GENMASK(63, 48)
|
||||
|
||||
enum rbep_msg_type {
|
||||
RBEP_INIT = 0x80,
|
||||
RBEP_INIT_ACK = 0xa0,
|
||||
RBEP_GETBUF = 0x89,
|
||||
RBEP_GETBUF_ACK = 0xa1,
|
||||
RBEP_INIT_TX = 0x8a,
|
||||
RBEP_INIT_RX = 0x8b,
|
||||
RBEP_START = 0xa3,
|
||||
RBEP_START_ACK = 0x86,
|
||||
RBEP_SEND = 0xa2,
|
||||
RBEP_RECV = 0x85,
|
||||
RBEP_SHUTDOWN = 0xc0,
|
||||
RBEP_SHUTDOWN_ACK = 0xc1,
|
||||
};
|
||||
|
||||
#define BLOCK_SHIFT 6
|
||||
|
||||
#define GETBUF_SIZE GENMASK(31, 16)
|
||||
#define GETBUF_TAG GENMASK(15, 0)
|
||||
#define GETBUF_ACK_DVA GENMASK(47, 0)
|
||||
|
||||
#define INITRB_OFFSET GENMASK(47, 32)
|
||||
#define INITRB_SIZE GENMASK(31, 16)
|
||||
#define INITRB_TAG GENMASK(15, 0)
|
||||
|
||||
#define SEND_WPTR GENMASK(31, 0)
|
||||
|
||||
static void afk_send(struct apple_dcp_afkep *ep, u64 message)
|
||||
{
|
||||
dcp_send_message(ep->dcp, ep->endpoint, message);
|
||||
}
|
||||
|
||||
struct apple_dcp_afkep *afk_init(struct apple_dcp *dcp, u32 endpoint,
|
||||
const struct apple_epic_service_ops *ops)
|
||||
{
|
||||
struct apple_dcp_afkep *afkep;
|
||||
int ret;
|
||||
|
||||
afkep = devm_kzalloc(dcp->dev, sizeof(*afkep), GFP_KERNEL);
|
||||
if (!afkep)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
afkep->ops = ops;
|
||||
afkep->dcp = dcp;
|
||||
afkep->endpoint = endpoint;
|
||||
afkep->wq = alloc_ordered_workqueue("apple-dcp-afkep%02x",
|
||||
WQ_MEM_RECLAIM, endpoint);
|
||||
if (!afkep->wq) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_afkep;
|
||||
}
|
||||
|
||||
// TODO: devm_ for wq
|
||||
|
||||
init_completion(&afkep->started);
|
||||
init_completion(&afkep->stopped);
|
||||
mtx_init(&afkep->lock, IPL_TTY);
|
||||
|
||||
return afkep;
|
||||
|
||||
out_free_afkep:
|
||||
devm_kfree(dcp->dev, afkep);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
int afk_start(struct apple_dcp_afkep *ep)
|
||||
{
|
||||
int ret;
|
||||
|
||||
reinit_completion(&ep->started);
|
||||
apple_rtkit_start_ep(ep->dcp->rtk, ep->endpoint);
|
||||
afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_INIT));
|
||||
|
||||
ret = wait_for_completion_timeout(&ep->started, msecs_to_jiffies(1000));
|
||||
if (ret <= 0)
|
||||
return -ETIMEDOUT;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void afk_getbuf(struct apple_dcp_afkep *ep, u64 message)
|
||||
{
|
||||
u16 size = FIELD_GET(GETBUF_SIZE, message) << BLOCK_SHIFT;
|
||||
u16 tag = FIELD_GET(GETBUF_TAG, message);
|
||||
u64 reply;
|
||||
|
||||
trace_afk_getbuf(ep, size, tag);
|
||||
|
||||
if (ep->bfr) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"Got GETBUF message but buffer already exists\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ep->bfr = dmam_alloc_coherent(ep->dcp->dev, size, &ep->bfr_dma,
|
||||
GFP_KERNEL);
|
||||
if (!ep->bfr) {
|
||||
dev_err(ep->dcp->dev, "Failed to allocate %d bytes buffer\n",
|
||||
size);
|
||||
return;
|
||||
}
|
||||
|
||||
ep->bfr_size = size;
|
||||
ep->bfr_tag = tag;
|
||||
|
||||
reply = FIELD_PREP(RBEP_TYPE, RBEP_GETBUF_ACK);
|
||||
reply |= FIELD_PREP(GETBUF_ACK_DVA, ep->bfr_dma);
|
||||
afk_send(ep, reply);
|
||||
}
|
||||
|
||||
static void afk_init_rxtx(struct apple_dcp_afkep *ep, u64 message,
|
||||
struct afk_ringbuffer *bfr)
|
||||
{
|
||||
u16 base = FIELD_GET(INITRB_OFFSET, message) << BLOCK_SHIFT;
|
||||
u16 size = FIELD_GET(INITRB_SIZE, message) << BLOCK_SHIFT;
|
||||
u16 tag = FIELD_GET(INITRB_TAG, message);
|
||||
u32 bufsz, end;
|
||||
|
||||
if (tag != ep->bfr_tag) {
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: expected tag 0x%x but got 0x%x",
|
||||
ep->endpoint, ep->bfr_tag, tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bfr->ready) {
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: buffer is already initialized\n",
|
||||
ep->endpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (base >= ep->bfr_size) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: requested base 0x%x >= max size 0x%lx",
|
||||
ep->endpoint, base, ep->bfr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
end = base + size;
|
||||
if (end > ep->bfr_size) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: requested end 0x%x > max size 0x%lx",
|
||||
ep->endpoint, end, ep->bfr_size);
|
||||
return;
|
||||
}
|
||||
|
||||
bfr->hdr = ep->bfr + base;
|
||||
bufsz = le32_to_cpu(bfr->hdr->bufsz);
|
||||
if (bufsz + sizeof(*bfr->hdr) != size) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: ring buffer size 0x%x != expected 0x%lx",
|
||||
ep->endpoint, bufsz, sizeof(*bfr->hdr));
|
||||
return;
|
||||
}
|
||||
|
||||
bfr->buf = bfr->hdr + 1;
|
||||
bfr->bufsz = bufsz;
|
||||
bfr->ready = true;
|
||||
|
||||
if (ep->rxbfr.ready && ep->txbfr.ready)
|
||||
afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_START));
|
||||
}
|
||||
|
||||
static const struct apple_epic_service_ops *
|
||||
afk_match_service(struct apple_dcp_afkep *ep, const char *name)
|
||||
{
|
||||
const struct apple_epic_service_ops *ops;
|
||||
|
||||
if (!name[0])
|
||||
return NULL;
|
||||
if (!ep->ops)
|
||||
return NULL;
|
||||
|
||||
for (ops = ep->ops; ops->name[0]; ops++) {
|
||||
if (strcmp(ops->name, name))
|
||||
continue;
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct apple_epic_service *afk_epic_find_service(struct apple_dcp_afkep *ep,
|
||||
u32 channel)
|
||||
{
|
||||
for (u32 i = 0; i < ep->num_channels; i++)
|
||||
if (ep->services[i].enabled && ep->services[i].channel == channel)
|
||||
return &ep->services[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void afk_recv_handle_init(struct apple_dcp_afkep *ep, u32 channel,
|
||||
u8 *payload, size_t payload_size)
|
||||
{
|
||||
char name[32];
|
||||
s64 epic_unit = -1;
|
||||
u32 ch_idx;
|
||||
const char *service_name = name;
|
||||
const char *epic_name = NULL, *epic_class = NULL;
|
||||
const struct apple_epic_service_ops *ops;
|
||||
struct dcp_parse_ctx ctx;
|
||||
u8 *props = payload + sizeof(name);
|
||||
size_t props_size = payload_size - sizeof(name);
|
||||
|
||||
WARN_ON(afk_epic_find_service(ep, channel));
|
||||
|
||||
if (payload_size < sizeof(name)) {
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: payload too small: %lx\n",
|
||||
ep->endpoint, payload_size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ep->num_channels >= AFK_MAX_CHANNEL) {
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: too many enabled services!\n",
|
||||
ep->endpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(name, payload, sizeof(name));
|
||||
|
||||
/*
|
||||
* in DCP firmware 13.2 DCP reports interface-name as name which starts
|
||||
* with "dispext%d" using -1 s ID for "dcp". In the 12.3 firmware
|
||||
* EPICProviderClass was used. If the init call has props parse them and
|
||||
* use EPICProviderClass to match the service.
|
||||
*/
|
||||
if (props_size > 36) {
|
||||
int ret = parse(props, props_size, &ctx);
|
||||
if (ret) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: Failed to parse service init props for %s\n",
|
||||
ep->endpoint, name);
|
||||
return;
|
||||
}
|
||||
ret = parse_epic_service_init(&ctx, &epic_name, &epic_class, &epic_unit);
|
||||
if (ret) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: failed to extract init props: %d\n",
|
||||
ep->endpoint, ret);
|
||||
return;
|
||||
}
|
||||
service_name = epic_class;
|
||||
} else {
|
||||
service_name = name;
|
||||
}
|
||||
|
||||
ops = afk_match_service(ep, service_name);
|
||||
if (!ops) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: unable to match service %s on channel %d\n",
|
||||
ep->endpoint, service_name, channel);
|
||||
goto free;
|
||||
}
|
||||
|
||||
ch_idx = ep->num_channels++;
|
||||
mtx_init(&ep->services[ch_idx].lock, IPL_TTY);
|
||||
ep->services[ch_idx].enabled = true;
|
||||
ep->services[ch_idx].ops = ops;
|
||||
ep->services[ch_idx].ep = ep;
|
||||
ep->services[ch_idx].channel = channel;
|
||||
ep->services[ch_idx].cmd_tag = 0;
|
||||
ops->init(&ep->services[ch_idx], epic_name, epic_class, epic_unit);
|
||||
dev_info(ep->dcp->dev, "AFK[ep:%02x]: new service %s on channel %d\n",
|
||||
ep->endpoint, service_name, channel);
|
||||
free:
|
||||
kfree(epic_name);
|
||||
kfree(epic_class);
|
||||
}
|
||||
|
||||
static void afk_recv_handle_teardown(struct apple_dcp_afkep *ep, u32 channel)
|
||||
{
|
||||
struct apple_epic_service *service;
|
||||
const struct apple_epic_service_ops *ops;
|
||||
unsigned long flags;
|
||||
|
||||
service = afk_epic_find_service(ep, channel);
|
||||
if (!service) {
|
||||
dev_warn(ep->dcp->dev, "AFK[ep:%02x]: teardown for disabled channel %u\n",
|
||||
ep->endpoint, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: think through what locking is necessary
|
||||
spin_lock_irqsave(&service->lock, flags);
|
||||
service->enabled = false;
|
||||
ops = service->ops;
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
|
||||
if (ops->teardown)
|
||||
ops->teardown(service);
|
||||
}
|
||||
|
||||
static void afk_recv_handle_reply(struct apple_dcp_afkep *ep, u32 channel,
|
||||
u16 tag, void *payload, size_t payload_size)
|
||||
{
|
||||
struct epic_cmd *cmd = payload;
|
||||
struct apple_epic_service *service;
|
||||
unsigned long flags;
|
||||
u8 idx = tag & 0xff;
|
||||
void *rxbuf, *txbuf;
|
||||
dma_addr_t rxbuf_dma, txbuf_dma;
|
||||
size_t rxlen, txlen;
|
||||
|
||||
service = afk_epic_find_service(ep, channel);
|
||||
if (!service) {
|
||||
dev_warn(ep->dcp->dev, "AFK[ep:%02x]: command reply on disabled channel %u\n",
|
||||
ep->endpoint, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload_size < sizeof(*cmd)) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: command reply on channel %d too small: %ld\n",
|
||||
ep->endpoint, channel, payload_size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx >= MAX_PENDING_CMDS) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: command reply on channel %d out of range: %d\n",
|
||||
ep->endpoint, channel, idx);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&service->lock, flags);
|
||||
if (service->cmds[idx].done) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: command reply on channel %d already handled\n",
|
||||
ep->endpoint, channel);
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tag != service->cmds[idx].tag) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: command reply on channel %d has invalid tag: expected 0x%04x != 0x%04x\n",
|
||||
ep->endpoint, channel, tag, service->cmds[idx].tag);
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
service->cmds[idx].done = true;
|
||||
service->cmds[idx].retcode = le32_to_cpu(cmd->retcode);
|
||||
if (service->cmds[idx].free_on_ack) {
|
||||
/* defer freeing until we're no longer in atomic context */
|
||||
rxbuf = service->cmds[idx].rxbuf;
|
||||
txbuf = service->cmds[idx].txbuf;
|
||||
rxlen = service->cmds[idx].rxlen;
|
||||
txlen = service->cmds[idx].txlen;
|
||||
rxbuf_dma = service->cmds[idx].rxbuf_dma;
|
||||
txbuf_dma = service->cmds[idx].txbuf_dma;
|
||||
bitmap_release_region(service->cmd_map, idx, 0);
|
||||
} else {
|
||||
rxbuf = txbuf = NULL;
|
||||
rxlen = txlen = 0;
|
||||
}
|
||||
if (service->cmds[idx].completion)
|
||||
complete(service->cmds[idx].completion);
|
||||
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
|
||||
if (rxbuf && rxlen)
|
||||
dma_free_coherent(ep->dcp->dev, rxlen, rxbuf, rxbuf_dma);
|
||||
if (txbuf && txlen)
|
||||
dma_free_coherent(ep->dcp->dev, txlen, txbuf, txbuf_dma);
|
||||
}
|
||||
|
||||
struct epic_std_service_ap_call {
|
||||
__le32 unk0;
|
||||
__le32 unk1;
|
||||
__le32 type;
|
||||
__le32 len;
|
||||
__le32 magic;
|
||||
u8 _unk[48];
|
||||
} __attribute__((packed));
|
||||
|
||||
static void afk_recv_handle_std_service(struct apple_dcp_afkep *ep, u32 channel,
|
||||
u32 type, struct epic_hdr *ehdr,
|
||||
struct epic_sub_hdr *eshdr,
|
||||
void *payload, size_t payload_size)
|
||||
{
|
||||
struct apple_epic_service *service = afk_epic_find_service(ep, channel);
|
||||
|
||||
if (!service) {
|
||||
dev_warn(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: std service notify on disabled channel %u\n",
|
||||
ep->endpoint, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_NOTIFY) {
|
||||
struct epic_std_service_ap_call *call = payload;
|
||||
size_t call_size;
|
||||
void *reply;
|
||||
int ret;
|
||||
|
||||
if (payload_size < sizeof(*call))
|
||||
return;
|
||||
|
||||
call_size = le32_to_cpu(call->len);
|
||||
if (payload_size < sizeof(*call) + call_size)
|
||||
return;
|
||||
|
||||
if (!service->ops->call)
|
||||
return;
|
||||
reply = kzalloc(payload_size, GFP_KERNEL);
|
||||
if (!reply)
|
||||
return;
|
||||
|
||||
ret = service->ops->call(service, le32_to_cpu(call->type),
|
||||
payload + sizeof(*call), call_size,
|
||||
reply + sizeof(*call), call_size);
|
||||
if (ret) {
|
||||
kfree(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(reply, call, sizeof(*call));
|
||||
afk_send_epic(ep, channel, le16_to_cpu(eshdr->tag),
|
||||
EPIC_TYPE_NOTIFY_ACK, EPIC_CAT_REPLY,
|
||||
EPIC_SUBTYPE_STD_SERVICE, reply, payload_size);
|
||||
kfree(reply);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_REPORT) {
|
||||
if (service->ops->report)
|
||||
service->ops->report(service, le16_to_cpu(eshdr->type),
|
||||
payload, payload_size);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: channel %d received unhandled standard service message: %x / %x\n",
|
||||
ep->endpoint, channel, type, eshdr->category);
|
||||
print_hex_dump(KERN_INFO, "AFK: ", DUMP_PREFIX_NONE, 16, 1, payload,
|
||||
payload_size, true);
|
||||
}
|
||||
|
||||
static void afk_recv_handle(struct apple_dcp_afkep *ep, u32 channel, u32 type,
|
||||
u8 *data, size_t data_size)
|
||||
{
|
||||
struct apple_epic_service *service;
|
||||
struct epic_hdr *ehdr = (struct epic_hdr *)data;
|
||||
struct epic_sub_hdr *eshdr =
|
||||
(struct epic_sub_hdr *)(data + sizeof(*ehdr));
|
||||
u16 subtype = le16_to_cpu(eshdr->type);
|
||||
u8 *payload = data + sizeof(*ehdr) + sizeof(*eshdr);
|
||||
size_t payload_size;
|
||||
|
||||
if (data_size < sizeof(*ehdr) + sizeof(*eshdr)) {
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: payload too small: %lx\n",
|
||||
ep->endpoint, data_size);
|
||||
return;
|
||||
}
|
||||
payload_size = data_size - sizeof(*ehdr) - sizeof(*eshdr);
|
||||
|
||||
trace_afk_recv_handle(ep, channel, type, data_size, ehdr, eshdr);
|
||||
|
||||
service = afk_epic_find_service(ep, channel);
|
||||
|
||||
if (!service) {
|
||||
if (type != EPIC_TYPE_NOTIFY && type != EPIC_TYPE_REPLY) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: expected notify but got 0x%x on channel %d\n",
|
||||
ep->endpoint, type, channel);
|
||||
return;
|
||||
}
|
||||
if (eshdr->category != EPIC_CAT_REPORT) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: expected report but got 0x%x on channel %d\n",
|
||||
ep->endpoint, eshdr->category, channel);
|
||||
return;
|
||||
}
|
||||
if (subtype == EPIC_SUBTYPE_TEARDOWN) {
|
||||
dev_dbg(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: teardown without service on channel %d\n",
|
||||
ep->endpoint, channel);
|
||||
return;
|
||||
}
|
||||
if (subtype != EPIC_SUBTYPE_ANNOUNCE) {
|
||||
dev_err(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: expected announce but got 0x%x on channel %d\n",
|
||||
ep->endpoint, subtype, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
return afk_recv_handle_init(ep, channel, payload, payload_size);
|
||||
}
|
||||
|
||||
if (!service) {
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d has no service\n",
|
||||
ep->endpoint, channel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == EPIC_TYPE_NOTIFY && eshdr->category == EPIC_CAT_REPORT &&
|
||||
subtype == EPIC_SUBTYPE_TEARDOWN)
|
||||
return afk_recv_handle_teardown(ep, channel);
|
||||
|
||||
if (type == EPIC_TYPE_REPLY && eshdr->category == EPIC_CAT_REPLY)
|
||||
return afk_recv_handle_reply(ep, channel,
|
||||
le16_to_cpu(eshdr->tag), payload,
|
||||
payload_size);
|
||||
|
||||
if (subtype == EPIC_SUBTYPE_STD_SERVICE)
|
||||
return afk_recv_handle_std_service(
|
||||
ep, channel, type, ehdr, eshdr, payload, payload_size);
|
||||
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: channel %d received unhandled message "
|
||||
"(type %x subtype %x)\n", ep->endpoint, channel, type, subtype);
|
||||
print_hex_dump(KERN_INFO, "AFK: ", DUMP_PREFIX_NONE, 16, 1, payload,
|
||||
payload_size, true);
|
||||
}
|
||||
|
||||
static bool afk_recv(struct apple_dcp_afkep *ep)
|
||||
{
|
||||
struct afk_qe *hdr;
|
||||
u32 rptr, wptr;
|
||||
u32 magic, size, channel, type;
|
||||
|
||||
if (!ep->rxbfr.ready) {
|
||||
dev_err(ep->dcp->dev, "AFK[ep:%02x]: got RECV but not ready\n",
|
||||
ep->endpoint);
|
||||
return false;
|
||||
}
|
||||
|
||||
rptr = le32_to_cpu(ep->rxbfr.hdr->rptr);
|
||||
wptr = le32_to_cpu(ep->rxbfr.hdr->wptr);
|
||||
trace_afk_recv_rwptr_pre(ep, rptr, wptr);
|
||||
|
||||
if (rptr == wptr)
|
||||
return false;
|
||||
|
||||
if (rptr > (ep->rxbfr.bufsz - sizeof(*hdr))) {
|
||||
dev_warn(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: rptr out of bounds: 0x%x > 0x%lx\n",
|
||||
ep->endpoint, rptr, ep->rxbfr.bufsz - sizeof(*hdr));
|
||||
return false;
|
||||
}
|
||||
|
||||
dma_rmb();
|
||||
|
||||
hdr = ep->rxbfr.buf + rptr;
|
||||
magic = le32_to_cpu(hdr->magic);
|
||||
size = le32_to_cpu(hdr->size);
|
||||
trace_afk_recv_qe(ep, rptr, magic, size);
|
||||
|
||||
if (magic != QE_MAGIC) {
|
||||
dev_warn(ep->dcp->dev, "AFK[ep:%02x]: invalid queue entry magic: 0x%x\n",
|
||||
ep->endpoint, magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's not enough space for the payload the co-processor inserted
|
||||
* the current dummy queue entry and we have to advance to the next one
|
||||
* which will contain the real data.
|
||||
*/
|
||||
if (rptr + size + sizeof(*hdr) > ep->rxbfr.bufsz) {
|
||||
rptr = 0;
|
||||
hdr = ep->rxbfr.buf + rptr;
|
||||
magic = le32_to_cpu(hdr->magic);
|
||||
size = le32_to_cpu(hdr->size);
|
||||
trace_afk_recv_qe(ep, rptr, magic, size);
|
||||
|
||||
if (magic != QE_MAGIC) {
|
||||
dev_warn(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: invalid next queue entry magic: 0x%x\n",
|
||||
ep->endpoint, magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
ep->rxbfr.hdr->rptr = cpu_to_le32(rptr);
|
||||
}
|
||||
|
||||
if (rptr + size + sizeof(*hdr) > ep->rxbfr.bufsz) {
|
||||
dev_warn(ep->dcp->dev,
|
||||
"AFK[ep:%02x]: queue entry out of bounds: 0x%lx > 0x%lx\n",
|
||||
ep->endpoint, rptr + size + sizeof(*hdr), ep->rxbfr.bufsz);
|
||||
return false;
|
||||
}
|
||||
|
||||
channel = le32_to_cpu(hdr->channel);
|
||||
type = le32_to_cpu(hdr->type);
|
||||
|
||||
rptr = ALIGN(rptr + sizeof(*hdr) + size, 1 << BLOCK_SHIFT);
|
||||
if (WARN_ON(rptr > ep->rxbfr.bufsz))
|
||||
rptr = 0;
|
||||
if (rptr == ep->rxbfr.bufsz)
|
||||
rptr = 0;
|
||||
|
||||
dma_mb();
|
||||
|
||||
ep->rxbfr.hdr->rptr = cpu_to_le32(rptr);
|
||||
trace_afk_recv_rwptr_post(ep, rptr, wptr);
|
||||
|
||||
/*
|
||||
* TODO: this is theoretically unsafe since DCP could overwrite data
|
||||
* after the read pointer was updated above. Do it anyway since
|
||||
* it avoids 2 problems in the DCP tracer:
|
||||
* 1. the tracer sees replies before the the notifies from dcp
|
||||
* 2. the tracer tries to read buffers after they are unmapped.
|
||||
*/
|
||||
afk_recv_handle(ep, channel, type, hdr->data, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void afk_receive_message_worker(struct work_struct *work_)
|
||||
{
|
||||
struct afk_receive_message_work *work;
|
||||
u16 type;
|
||||
|
||||
work = container_of(work_, struct afk_receive_message_work, work);
|
||||
|
||||
type = FIELD_GET(RBEP_TYPE, work->message);
|
||||
switch (type) {
|
||||
case RBEP_INIT_ACK:
|
||||
break;
|
||||
|
||||
case RBEP_START_ACK:
|
||||
complete_all(&work->ep->started);
|
||||
break;
|
||||
|
||||
case RBEP_SHUTDOWN_ACK:
|
||||
complete_all(&work->ep->stopped);
|
||||
break;
|
||||
|
||||
case RBEP_GETBUF:
|
||||
afk_getbuf(work->ep, work->message);
|
||||
break;
|
||||
|
||||
case RBEP_INIT_TX:
|
||||
afk_init_rxtx(work->ep, work->message, &work->ep->txbfr);
|
||||
break;
|
||||
|
||||
case RBEP_INIT_RX:
|
||||
afk_init_rxtx(work->ep, work->message, &work->ep->rxbfr);
|
||||
break;
|
||||
|
||||
case RBEP_RECV:
|
||||
while (afk_recv(work->ep))
|
||||
;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(work->ep->dcp->dev,
|
||||
"Received unknown AFK message type: 0x%x\n", type);
|
||||
}
|
||||
|
||||
kfree(work);
|
||||
}
|
||||
|
||||
int afk_receive_message(struct apple_dcp_afkep *ep, u64 message)
|
||||
{
|
||||
struct afk_receive_message_work *work;
|
||||
|
||||
// TODO: comment why decoupling from rtkit thread is required here
|
||||
work = kzalloc(sizeof(*work), GFP_KERNEL);
|
||||
if (!work)
|
||||
return -ENOMEM;
|
||||
|
||||
work->ep = ep;
|
||||
work->message = message;
|
||||
INIT_WORK(&work->work, afk_receive_message_worker);
|
||||
queue_work(ep->wq, &work->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int afk_send_epic(struct apple_dcp_afkep *ep, u32 channel, u16 tag,
|
||||
enum epic_type etype, enum epic_category ecat, u8 stype,
|
||||
const void *payload, size_t payload_len)
|
||||
{
|
||||
u32 rptr, wptr;
|
||||
struct afk_qe *hdr, *hdr2;
|
||||
struct epic_hdr *ehdr;
|
||||
struct epic_sub_hdr *eshdr;
|
||||
unsigned long flags;
|
||||
size_t total_epic_size, total_size;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
|
||||
dma_rmb();
|
||||
rptr = le32_to_cpu(ep->txbfr.hdr->rptr);
|
||||
wptr = le32_to_cpu(ep->txbfr.hdr->wptr);
|
||||
trace_afk_send_rwptr_pre(ep, rptr, wptr);
|
||||
total_epic_size = sizeof(*ehdr) + sizeof(*eshdr) + payload_len;
|
||||
total_size = sizeof(*hdr) + total_epic_size;
|
||||
|
||||
hdr = hdr2 = NULL;
|
||||
|
||||
/*
|
||||
* We need to figure out how to place the entire headers and payload
|
||||
* into the ring buffer:
|
||||
* - If the write pointer is in front of the read pointer we just need
|
||||
* enough space inbetween to store everything.
|
||||
* - If the read pointer has already wrapper around the end of the
|
||||
* buffer we can
|
||||
* a) either store the entire payload at the writer pointer if
|
||||
* there's enough space until the end,
|
||||
* b) or just store the queue entry at the write pointer to indicate
|
||||
* that we need to wrap to the start and then store the headers
|
||||
* and the payload at the beginning of the buffer. The queue
|
||||
* header has to be store twice in this case.
|
||||
* In either case we have to ensure that there's always enough space
|
||||
* so that we don't accidentally overwrite other buffers.
|
||||
*/
|
||||
if (wptr < rptr) {
|
||||
/*
|
||||
* If wptr < rptr we can't wrap around and only have to make
|
||||
* sure that there's enough space for the entire payload.
|
||||
*/
|
||||
if (wptr + total_size > rptr) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
hdr = ep->txbfr.buf + wptr;
|
||||
wptr += sizeof(*hdr);
|
||||
} else {
|
||||
/* We need enough space to place at least a queue entry */
|
||||
if (wptr + sizeof(*hdr) > ep->txbfr.bufsz) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we can place a single queue entry but not the full payload
|
||||
* we need to place one queue entry at the end of the ring
|
||||
* buffer and then another one together with the entire
|
||||
* payload at the beginning.
|
||||
*/
|
||||
if (wptr + total_size > ep->txbfr.bufsz) {
|
||||
/*
|
||||
* Ensure there's space for the queue entry at the
|
||||
* beginning
|
||||
*/
|
||||
if (sizeof(*hdr) > rptr) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Place two queue entries to indicate we want to wrap
|
||||
* around to the firmware.
|
||||
*/
|
||||
hdr = ep->txbfr.buf + wptr;
|
||||
hdr2 = ep->txbfr.buf;
|
||||
wptr = sizeof(*hdr);
|
||||
|
||||
/* Ensure there's enough space for the entire payload */
|
||||
if (wptr + total_epic_size > rptr) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
/* We have enough space to place the entire payload */
|
||||
hdr = ep->txbfr.buf + wptr;
|
||||
wptr += sizeof(*hdr);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* At this point we're guaranteed that hdr (and possibly hdr2) point
|
||||
* to a buffer large enough to fit the queue entry and that we have
|
||||
* enough space at wptr to store the payload.
|
||||
*/
|
||||
|
||||
hdr->magic = cpu_to_le32(QE_MAGIC);
|
||||
hdr->size = cpu_to_le32(total_epic_size);
|
||||
hdr->channel = cpu_to_le32(channel);
|
||||
hdr->type = cpu_to_le32(etype);
|
||||
if (hdr2)
|
||||
memcpy(hdr2, hdr, sizeof(*hdr));
|
||||
|
||||
ehdr = ep->txbfr.buf + wptr;
|
||||
memset(ehdr, 0, sizeof(*ehdr));
|
||||
ehdr->version = 2;
|
||||
ehdr->seq = cpu_to_le16(ep->qe_seq++);
|
||||
ehdr->timestamp = cpu_to_le64(0);
|
||||
wptr += sizeof(*ehdr);
|
||||
|
||||
eshdr = ep->txbfr.buf + wptr;
|
||||
memset(eshdr, 0, sizeof(*eshdr));
|
||||
eshdr->length = cpu_to_le32(payload_len);
|
||||
eshdr->version = 4;
|
||||
eshdr->category = ecat;
|
||||
eshdr->type = cpu_to_le16(stype);
|
||||
eshdr->timestamp = cpu_to_le64(0);
|
||||
eshdr->tag = cpu_to_le16(tag);
|
||||
if (ecat == EPIC_CAT_REPLY)
|
||||
eshdr->inline_len = cpu_to_le16(payload_len - 4);
|
||||
else
|
||||
eshdr->inline_len = cpu_to_le16(0);
|
||||
wptr += sizeof(*eshdr);
|
||||
|
||||
memcpy(ep->txbfr.buf + wptr, payload, payload_len);
|
||||
wptr += payload_len;
|
||||
wptr = ALIGN(wptr, 1 << BLOCK_SHIFT);
|
||||
if (wptr == ep->txbfr.bufsz)
|
||||
wptr = 0;
|
||||
trace_afk_send_rwptr_post(ep, rptr, wptr);
|
||||
|
||||
ep->txbfr.hdr->wptr = cpu_to_le32(wptr);
|
||||
afk_send(ep, FIELD_PREP(RBEP_TYPE, RBEP_SEND) |
|
||||
FIELD_PREP(SEND_WPTR, wptr));
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int afk_send_command(struct apple_epic_service *service, u8 type,
|
||||
const void *payload, size_t payload_len, void *output,
|
||||
size_t output_len, u32 *retcode)
|
||||
{
|
||||
struct epic_cmd cmd;
|
||||
void *rxbuf, *txbuf;
|
||||
dma_addr_t rxbuf_dma, txbuf_dma;
|
||||
unsigned long flags;
|
||||
int ret, idx;
|
||||
u16 tag;
|
||||
struct apple_dcp_afkep *ep = service->ep;
|
||||
DECLARE_COMPLETION_ONSTACK(completion);
|
||||
|
||||
rxbuf = dma_alloc_coherent(ep->dcp->dev, output_len, &rxbuf_dma,
|
||||
GFP_KERNEL);
|
||||
if (!rxbuf)
|
||||
return -ENOMEM;
|
||||
txbuf = dma_alloc_coherent(ep->dcp->dev, payload_len, &txbuf_dma,
|
||||
GFP_KERNEL);
|
||||
if (!txbuf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_rxbuf;
|
||||
}
|
||||
|
||||
memcpy(txbuf, payload, payload_len);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.retcode = cpu_to_le32(0);
|
||||
cmd.rxbuf = cpu_to_le64(rxbuf_dma);
|
||||
cmd.rxlen = cpu_to_le32(output_len);
|
||||
cmd.txbuf = cpu_to_le64(txbuf_dma);
|
||||
cmd.txlen = cpu_to_le32(payload_len);
|
||||
|
||||
spin_lock_irqsave(&service->lock, flags);
|
||||
idx = bitmap_find_free_region(service->cmd_map, MAX_PENDING_CMDS, 0);
|
||||
if (idx < 0) {
|
||||
ret = -ENOSPC;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
tag = (service->cmd_tag & 0xff) << 8;
|
||||
tag |= idx & 0xff;
|
||||
service->cmd_tag++;
|
||||
|
||||
service->cmds[idx].tag = tag;
|
||||
service->cmds[idx].rxbuf = rxbuf;
|
||||
service->cmds[idx].txbuf = txbuf;
|
||||
service->cmds[idx].rxbuf_dma = rxbuf_dma;
|
||||
service->cmds[idx].txbuf_dma = txbuf_dma;
|
||||
service->cmds[idx].rxlen = output_len;
|
||||
service->cmds[idx].txlen = payload_len;
|
||||
service->cmds[idx].free_on_ack = false;
|
||||
service->cmds[idx].done = false;
|
||||
service->cmds[idx].completion = &completion;
|
||||
init_completion(&completion);
|
||||
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
|
||||
ret = afk_send_epic(service->ep, service->channel, tag,
|
||||
EPIC_TYPE_COMMAND, EPIC_CAT_COMMAND, type, &cmd,
|
||||
sizeof(cmd));
|
||||
if (ret)
|
||||
goto err_free_cmd;
|
||||
|
||||
ret = wait_for_completion_timeout(&completion,
|
||||
msecs_to_jiffies(MSEC_PER_SEC));
|
||||
|
||||
if (ret <= 0) {
|
||||
spin_lock_irqsave(&service->lock, flags);
|
||||
/*
|
||||
* Check again while we're inside the lock to make sure
|
||||
* the command wasn't completed just after
|
||||
* wait_for_completion_timeout returned.
|
||||
*/
|
||||
if (!service->cmds[idx].done) {
|
||||
service->cmds[idx].completion = NULL;
|
||||
service->cmds[idx].free_on_ack = true;
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
if (retcode)
|
||||
*retcode = service->cmds[idx].retcode;
|
||||
if (output && output_len)
|
||||
memcpy(output, rxbuf, output_len);
|
||||
|
||||
err_free_cmd:
|
||||
spin_lock_irqsave(&service->lock, flags);
|
||||
bitmap_release_region(service->cmd_map, idx, 0);
|
||||
err_unlock:
|
||||
spin_unlock_irqrestore(&service->lock, flags);
|
||||
dma_free_coherent(ep->dcp->dev, payload_len, txbuf, txbuf_dma);
|
||||
err_free_rxbuf:
|
||||
dma_free_coherent(ep->dcp->dev, output_len, rxbuf, rxbuf_dma);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int afk_service_call(struct apple_epic_service *service, u16 group, u32 command,
|
||||
const void *data, size_t data_len, size_t data_pad,
|
||||
void *output, size_t output_len, size_t output_pad)
|
||||
{
|
||||
struct epic_service_call *call;
|
||||
void *bfr;
|
||||
size_t bfr_len = max(data_len + data_pad, output_len + output_pad) +
|
||||
sizeof(*call);
|
||||
int ret;
|
||||
u32 retcode;
|
||||
u32 retlen;
|
||||
|
||||
bfr = kzalloc(bfr_len, GFP_KERNEL);
|
||||
if (!bfr)
|
||||
return -ENOMEM;
|
||||
|
||||
call = bfr;
|
||||
|
||||
memset(call, 0, sizeof(*call));
|
||||
call->group = cpu_to_le16(group);
|
||||
call->command = cpu_to_le32(command);
|
||||
call->data_len = cpu_to_le32(data_len + data_pad);
|
||||
call->magic = cpu_to_le32(EPIC_SERVICE_CALL_MAGIC);
|
||||
|
||||
memcpy(bfr + sizeof(*call), data, data_len);
|
||||
|
||||
ret = afk_send_command(service, EPIC_SUBTYPE_STD_SERVICE, bfr, bfr_len,
|
||||
bfr, bfr_len, &retcode);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (retcode) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (le32_to_cpu(call->magic) != EPIC_SERVICE_CALL_MAGIC ||
|
||||
le16_to_cpu(call->group) != group ||
|
||||
le32_to_cpu(call->command) != command) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retlen = le32_to_cpu(call->data_len);
|
||||
if (output_len < retlen)
|
||||
retlen = output_len;
|
||||
if (output && output_len) {
|
||||
memset(output, 0, output_len);
|
||||
memcpy(output, bfr + sizeof(*call), retlen);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(bfr);
|
||||
return ret;
|
||||
}
|
192
sys/dev/pci/drm/apple/afk.h
Normal file
192
sys/dev/pci/drm/apple/afk.h
Normal file
|
@ -0,0 +1,192 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/*
|
||||
* AFK (Apple Firmware Kit) EPIC (EndPoint Interface Client) support
|
||||
*/
|
||||
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
|
||||
|
||||
#ifndef _DRM_APPLE_DCP_AFK_H
|
||||
#define _DRM_APPLE_DCP_AFK_H
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "dcp.h"
|
||||
|
||||
#define AFK_MAX_CHANNEL 16
|
||||
#define MAX_PENDING_CMDS 16
|
||||
|
||||
struct apple_epic_service_ops;
|
||||
struct apple_dcp_afkep;
|
||||
|
||||
struct epic_cmd_info {
|
||||
u16 tag;
|
||||
|
||||
void *rxbuf;
|
||||
void *txbuf;
|
||||
dma_addr_t rxbuf_dma;
|
||||
dma_addr_t txbuf_dma;
|
||||
size_t rxlen;
|
||||
size_t txlen;
|
||||
|
||||
u32 retcode;
|
||||
bool done;
|
||||
bool free_on_ack;
|
||||
struct completion *completion;
|
||||
};
|
||||
|
||||
struct apple_epic_service {
|
||||
const struct apple_epic_service_ops *ops;
|
||||
struct apple_dcp_afkep *ep;
|
||||
|
||||
struct epic_cmd_info cmds[MAX_PENDING_CMDS];
|
||||
DECLARE_BITMAP(cmd_map, MAX_PENDING_CMDS);
|
||||
u8 cmd_tag;
|
||||
spinlock_t lock;
|
||||
|
||||
u32 channel;
|
||||
bool enabled;
|
||||
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
enum epic_subtype;
|
||||
|
||||
struct apple_epic_service_ops {
|
||||
const char name[32];
|
||||
|
||||
void (*init)(struct apple_epic_service *service, const char *name,
|
||||
const char *class, s64 unit);
|
||||
int (*call)(struct apple_epic_service *service, u32 idx,
|
||||
const void *data, size_t data_size, void *reply,
|
||||
size_t reply_size);
|
||||
int (*report)(struct apple_epic_service *service, enum epic_subtype type,
|
||||
const void *data, size_t data_size);
|
||||
void (*teardown)(struct apple_epic_service *service);
|
||||
};
|
||||
|
||||
struct afk_ringbuffer_header {
|
||||
__le32 bufsz;
|
||||
u32 unk;
|
||||
u32 _pad1[14];
|
||||
__le32 rptr;
|
||||
u32 _pad2[15];
|
||||
__le32 wptr;
|
||||
u32 _pad3[15];
|
||||
};
|
||||
|
||||
struct afk_qe {
|
||||
#define QE_MAGIC 0x20504f49 // ' POI'
|
||||
__le32 magic;
|
||||
__le32 size;
|
||||
__le32 channel;
|
||||
__le32 type;
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
struct epic_hdr {
|
||||
u8 version;
|
||||
__le16 seq;
|
||||
u8 _pad;
|
||||
__le32 unk;
|
||||
__le64 timestamp;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct epic_sub_hdr {
|
||||
__le32 length;
|
||||
u8 version;
|
||||
u8 category;
|
||||
__le16 type;
|
||||
__le64 timestamp;
|
||||
__le16 tag;
|
||||
__le16 unk;
|
||||
__le32 inline_len;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct epic_cmd {
|
||||
__le32 retcode;
|
||||
__le64 rxbuf;
|
||||
__le64 txbuf;
|
||||
__le32 rxlen;
|
||||
__le32 txlen;
|
||||
u8 rxcookie;
|
||||
u8 txcookie;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct epic_service_call {
|
||||
u8 _pad0[2];
|
||||
__le16 group;
|
||||
__le32 command;
|
||||
__le32 data_len;
|
||||
#define EPIC_SERVICE_CALL_MAGIC 0x69706378
|
||||
__le32 magic;
|
||||
u8 _pad1[48];
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(struct epic_service_call) == 64);
|
||||
|
||||
enum epic_type {
|
||||
EPIC_TYPE_NOTIFY = 0,
|
||||
EPIC_TYPE_COMMAND = 3,
|
||||
EPIC_TYPE_REPLY = 4,
|
||||
EPIC_TYPE_NOTIFY_ACK = 8,
|
||||
};
|
||||
|
||||
enum epic_category {
|
||||
EPIC_CAT_REPORT = 0x00,
|
||||
EPIC_CAT_NOTIFY = 0x10,
|
||||
EPIC_CAT_REPLY = 0x20,
|
||||
EPIC_CAT_COMMAND = 0x30,
|
||||
};
|
||||
|
||||
enum epic_subtype {
|
||||
EPIC_SUBTYPE_ANNOUNCE = 0x30,
|
||||
EPIC_SUBTYPE_TEARDOWN = 0x32,
|
||||
EPIC_SUBTYPE_STD_SERVICE = 0xc0,
|
||||
};
|
||||
|
||||
struct afk_ringbuffer {
|
||||
bool ready;
|
||||
struct afk_ringbuffer_header *hdr;
|
||||
u32 rptr;
|
||||
void *buf;
|
||||
size_t bufsz;
|
||||
};
|
||||
|
||||
struct apple_dcp_afkep {
|
||||
struct apple_dcp *dcp;
|
||||
|
||||
u32 endpoint;
|
||||
struct workqueue_struct *wq;
|
||||
|
||||
struct completion started;
|
||||
struct completion stopped;
|
||||
|
||||
void *bfr;
|
||||
u16 bfr_tag;
|
||||
size_t bfr_size;
|
||||
dma_addr_t bfr_dma;
|
||||
|
||||
struct afk_ringbuffer txbfr;
|
||||
struct afk_ringbuffer rxbfr;
|
||||
|
||||
spinlock_t lock;
|
||||
u16 qe_seq;
|
||||
|
||||
const struct apple_epic_service_ops *ops;
|
||||
struct apple_epic_service services[AFK_MAX_CHANNEL];
|
||||
u32 num_channels;
|
||||
};
|
||||
|
||||
struct apple_dcp_afkep *afk_init(struct apple_dcp *dcp, u32 endpoint,
|
||||
const struct apple_epic_service_ops *ops);
|
||||
int afk_start(struct apple_dcp_afkep *ep);
|
||||
int afk_receive_message(struct apple_dcp_afkep *ep, u64 message);
|
||||
int afk_send_epic(struct apple_dcp_afkep *ep, u32 channel, u16 tag,
|
||||
enum epic_type etype, enum epic_category ecat, u8 stype,
|
||||
const void *payload, size_t payload_len);
|
||||
int afk_send_command(struct apple_epic_service *service, u8 type,
|
||||
const void *payload, size_t payload_len, void *output,
|
||||
size_t output_len, u32 *retcode);
|
||||
int afk_service_call(struct apple_epic_service *service, u16 group, u32 command,
|
||||
const void *data, size_t data_len, size_t data_pad,
|
||||
void *output, size_t output_len, size_t output_pad);
|
||||
#endif
|
267
sys/dev/pci/drm/apple/apldcp.c
Normal file
267
sys/dev/pci/drm/apple/apldcp.c
Normal file
|
@ -0,0 +1,267 @@
|
|||
/* $OpenBSD: apldcp.c,v 1.1 2024/01/22 18:54:01 kettenis Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
|
||||
#include <machine/intr.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/fdt.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/fdt.h>
|
||||
#include <dev/ofw/ofw_power.h>
|
||||
#include <dev/ofw/ofw_clock.h>
|
||||
|
||||
static const void *of_device_get_match_data(const struct device *);
|
||||
|
||||
#include "dcp.c"
|
||||
|
||||
struct apldcp_softc {
|
||||
struct platform_device sc_dev;
|
||||
};
|
||||
|
||||
int apldcp_match(struct device *, void *, void *);
|
||||
void apldcp_attach(struct device *, struct device *, void *);
|
||||
int apldcp_activate(struct device *, int);
|
||||
|
||||
const struct cfattach apldcp_ca = {
|
||||
sizeof (struct apldcp_softc), apldcp_match, apldcp_attach,
|
||||
NULL, apldcp_activate
|
||||
};
|
||||
|
||||
struct cfdriver apldcp_cd = {
|
||||
NULL, "apldcp", DV_DULL
|
||||
};
|
||||
|
||||
int
|
||||
apldcp_match(struct device *parent, void *match, void *aux)
|
||||
{
|
||||
struct fdt_attach_args *faa = aux;
|
||||
|
||||
return OF_is_compatible(faa->fa_node, "apple,dcp") ||
|
||||
OF_is_compatible(faa->fa_node, "apple,dcpext");
|
||||
}
|
||||
|
||||
void
|
||||
apldcp_attach(struct device *parent, struct device *self, void *aux)
|
||||
{
|
||||
struct apldcp_softc *sc = (struct apldcp_softc *)self;
|
||||
struct fdt_attach_args *faa = aux;
|
||||
|
||||
power_domain_enable(faa->fa_node);
|
||||
reset_deassert_all(faa->fa_node);
|
||||
|
||||
printf("\n");
|
||||
|
||||
sc->sc_dev.faa = faa;
|
||||
platform_device_register(&sc->sc_dev);
|
||||
|
||||
dcp_platform_probe(&sc->sc_dev);
|
||||
}
|
||||
|
||||
int
|
||||
apldcp_activate(struct device *self, int act)
|
||||
{
|
||||
int rv;
|
||||
|
||||
switch (act) {
|
||||
case DVACT_QUIESCE:
|
||||
rv = config_activate_children(self, act);
|
||||
dcp_platform_suspend(self);
|
||||
break;
|
||||
case DVACT_WAKEUP:
|
||||
dcp_platform_resume(self);
|
||||
rv = config_activate_children(self, act);
|
||||
break;
|
||||
default:
|
||||
rv = config_activate_children(self, act);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux RTKit interfaces.
|
||||
*/
|
||||
|
||||
#include <arm64/dev/rtkit.h>
|
||||
|
||||
struct apple_rtkit_ep {
|
||||
struct apple_rtkit *rtk;
|
||||
uint8_t ep;
|
||||
|
||||
struct task task;
|
||||
uint64_t msg;
|
||||
};
|
||||
|
||||
struct apple_rtkit {
|
||||
struct rtkit_state *state;
|
||||
struct apple_rtkit_ep ep[64];
|
||||
void *cookie;
|
||||
struct platform_device *pdev;
|
||||
const struct apple_rtkit_ops *ops;
|
||||
struct taskq *tq;
|
||||
};
|
||||
|
||||
paddr_t
|
||||
apple_rtkit_logmap(void *cookie, bus_addr_t addr)
|
||||
{
|
||||
struct apple_rtkit *rtk = cookie;
|
||||
int idx, len, node;
|
||||
uint32_t *phandles;
|
||||
uint32_t iommu_addresses[5];
|
||||
bus_addr_t start;
|
||||
bus_size_t size;
|
||||
uint64_t reg[2];
|
||||
|
||||
len = OF_getproplen(rtk->pdev->node, "memory-region");
|
||||
idx = OF_getindex(rtk->pdev->node, "dcp_data", "memory-region-names");
|
||||
if (idx < 0 || idx >= len / sizeof(uint32_t))
|
||||
return addr;
|
||||
|
||||
phandles = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
|
||||
OF_getpropintarray(rtk->pdev->node, "memory-region",
|
||||
phandles, len);
|
||||
node = OF_getnodebyphandle(phandles[idx]);
|
||||
free(phandles, M_TEMP, len);
|
||||
|
||||
if (node == 0)
|
||||
return addr;
|
||||
|
||||
if (!OF_is_compatible(node, "apple,asc-mem"))
|
||||
return addr;
|
||||
|
||||
if (OF_getpropint64array(node, "reg", reg, sizeof(reg)) != sizeof(reg))
|
||||
return addr;
|
||||
|
||||
if (OF_getpropintarray(node, "iommu-addresses", iommu_addresses,
|
||||
sizeof(iommu_addresses)) < sizeof(iommu_addresses))
|
||||
return addr;
|
||||
start = (uint64_t)iommu_addresses[1] << 32 | iommu_addresses[2];
|
||||
size = (uint64_t)iommu_addresses[3] << 32 | iommu_addresses[4];
|
||||
if (addr >= start && addr < start + size)
|
||||
return reg[0] + (addr - start);
|
||||
|
||||
/* XXX some machines have truncated DVAs in "iommu-addresses" */
|
||||
addr &= 0xffffffff;
|
||||
if (addr >= start && addr < start + size)
|
||||
return reg[0] + (addr - start);
|
||||
|
||||
return (paddr_t)-1;
|
||||
}
|
||||
|
||||
void
|
||||
apple_rtkit_do_recv(void *arg)
|
||||
{
|
||||
struct apple_rtkit_ep *rtkep = arg;
|
||||
struct apple_rtkit *rtk = rtkep->rtk;
|
||||
|
||||
rtk->ops->recv_message(rtk->cookie, rtkep->ep, rtkep->msg);
|
||||
}
|
||||
|
||||
void
|
||||
apple_rtkit_recv(void *cookie, uint64_t msg)
|
||||
{
|
||||
struct apple_rtkit_ep *rtkep = cookie;
|
||||
struct apple_rtkit *rtk = rtkep->rtk;
|
||||
|
||||
rtkep->msg = msg;
|
||||
task_add(rtk->tq, &rtkep->task);
|
||||
}
|
||||
|
||||
int
|
||||
apple_rtkit_start_ep(struct apple_rtkit *rtk, uint8_t ep)
|
||||
{
|
||||
struct apple_rtkit_ep *rtkep;
|
||||
int error;
|
||||
|
||||
rtkep = &rtk->ep[ep];
|
||||
rtkep->rtk = rtk;
|
||||
rtkep->ep = ep;
|
||||
task_set(&rtkep->task, apple_rtkit_do_recv, rtkep);
|
||||
|
||||
error = rtkit_start_endpoint(rtk->state, ep, apple_rtkit_recv, rtkep);
|
||||
return -error;
|
||||
}
|
||||
|
||||
int
|
||||
apple_rtkit_send_message(struct apple_rtkit *rtk, uint8_t ep, uint64_t msg,
|
||||
struct completion *completion, int atomic)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = rtkit_send_endpoint(rtk->state, ep, msg);
|
||||
return -error;
|
||||
}
|
||||
|
||||
int
|
||||
apple_rtkit_wake(struct apple_rtkit *rtk)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = rtkit_set_iop_pwrstate(rtk->state, RTKIT_MGMT_PWR_STATE_INIT);
|
||||
if (error)
|
||||
return -error;
|
||||
|
||||
error = rtkit_set_ap_pwrstate(rtk->state, RTKIT_MGMT_PWR_STATE_ON);
|
||||
return -error;
|
||||
}
|
||||
|
||||
struct apple_rtkit *
|
||||
devm_apple_rtkit_init(struct device *dev, void *cookie,
|
||||
const char *mbox_name, int mbox_idx, const struct apple_rtkit_ops *ops)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device *)dev;
|
||||
struct apple_rtkit *rtk;
|
||||
struct rtkit *rk;
|
||||
|
||||
rtk = malloc(sizeof(*rtk), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
rtk->tq = taskq_create("drmrtk", 1, IPL_HIGH, 0);
|
||||
if (rtk->tq == NULL) {
|
||||
free(rtk, M_DEVBUF, sizeof(*rtk));
|
||||
return ERR_PTR(ENOMEM);
|
||||
}
|
||||
|
||||
rk = malloc(sizeof(*rk), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
rk->rk_cookie = rtk;
|
||||
rk->rk_dmat = pdev->dmat;
|
||||
rk->rk_logmap = apple_rtkit_logmap;
|
||||
|
||||
rtk->state = rtkit_init(pdev->node, mbox_name, 0, rk);
|
||||
rtk->cookie = cookie;
|
||||
rtk->pdev = pdev;
|
||||
rtk->ops = ops;
|
||||
|
||||
return rtk;
|
||||
}
|
||||
|
||||
static const void *
|
||||
of_device_get_match_data(const struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device *)dev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nitems(of_match); i++) {
|
||||
if (OF_is_compatible(pdev->node, of_match[i].compatible))
|
||||
return of_match[i].data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
401
sys/dev/pci/drm/apple/apldrm.c
Normal file
401
sys/dev/pci/drm/apple/apldrm.c
Normal file
|
@ -0,0 +1,401 @@
|
|||
/* $OpenBSD: apldrm.c,v 1.1 2024/01/22 18:54:01 kettenis Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/device.h>
|
||||
|
||||
#include <machine/fdt.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/fdt.h>
|
||||
|
||||
#include <dev/wscons/wsconsio.h>
|
||||
#include <dev/wscons/wsdisplayvar.h>
|
||||
#include <dev/rasops/rasops.h>
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
|
||||
struct apldrm_softc {
|
||||
struct platform_device sc_dev;
|
||||
struct drm_device sc_ddev;
|
||||
|
||||
int sc_node;
|
||||
|
||||
struct rasops_info sc_ri;
|
||||
struct wsscreen_descr sc_wsd;
|
||||
struct wsscreen_list sc_wsl;
|
||||
struct wsscreen_descr *sc_scrlist[1];
|
||||
|
||||
void (*sc_switchcb)(void *, int, int);
|
||||
void *sc_switchcbarg;
|
||||
void *sc_switchcookie;
|
||||
struct task sc_switchtask;
|
||||
|
||||
int sc_burner_fblank;
|
||||
struct task sc_burner_task;
|
||||
};
|
||||
|
||||
#include "apple_drv.c"
|
||||
|
||||
int apldrm_match(struct device *, void *, void *);
|
||||
void apldrm_attach(struct device *, struct device *, void *);
|
||||
int apldrm_activate(struct device *, int);
|
||||
|
||||
const struct cfattach apldrm_ca = {
|
||||
sizeof (struct apldrm_softc), apldrm_match, apldrm_attach,
|
||||
NULL, apldrm_activate
|
||||
};
|
||||
|
||||
struct cfdriver apldrm_cd = {
|
||||
NULL, "apldrm", DV_DULL
|
||||
};
|
||||
|
||||
void apldrm_attachhook(struct device *);
|
||||
|
||||
int
|
||||
apldrm_match(struct device *parent, void *match, void *aux)
|
||||
{
|
||||
struct fdt_attach_args *faa = aux;
|
||||
|
||||
return OF_is_compatible(faa->fa_node, "apple,display-subsystem");
|
||||
}
|
||||
|
||||
void
|
||||
apldrm_attach(struct device *parent, struct device *self, void *aux)
|
||||
{
|
||||
struct apldrm_softc *sc = (struct apldrm_softc *)self;
|
||||
struct fdt_attach_args *faa = aux;
|
||||
int idx, len, node;
|
||||
uint32_t *phandles;
|
||||
uint64_t reg[2];
|
||||
|
||||
sc->sc_node = faa->fa_node;
|
||||
|
||||
/* Claim framebuffer to prevent attaching other drivers. */
|
||||
len = OF_getproplen(faa->fa_node, "memory-region");
|
||||
idx = OF_getindex(faa->fa_node, "framebuffer", "memory-region-names");
|
||||
if (idx >= 0 && idx < len / sizeof(uint32_t)) {
|
||||
phandles = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
|
||||
OF_getpropintarray(faa->fa_node, "memory-region",
|
||||
phandles, len);
|
||||
node = OF_getnodebyphandle(phandles[idx]);
|
||||
if (node) {
|
||||
if (OF_getpropint64array(node, "reg", reg,
|
||||
sizeof(reg)) == sizeof(reg))
|
||||
rasops_claim_framebuffer(reg[0], reg[1], self);
|
||||
}
|
||||
free(phandles, M_TEMP, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update our understanding of the console output node if
|
||||
* we're using the framebuffer console.
|
||||
*/
|
||||
if (OF_is_compatible(stdout_node, "simple-framebuffer"))
|
||||
stdout_node = sc->sc_node;
|
||||
|
||||
printf("\n");
|
||||
|
||||
sc->sc_dev.faa = faa;
|
||||
platform_device_register(&sc->sc_dev);
|
||||
|
||||
drm_attach_platform((struct drm_driver *)&apple_drm_driver,
|
||||
faa->fa_iot, faa->fa_dmat, self, &sc->sc_ddev);
|
||||
config_mountroot(self, apldrm_attachhook);
|
||||
}
|
||||
|
||||
int
|
||||
apldrm_activate(struct device *self, int act)
|
||||
{
|
||||
int rv;
|
||||
|
||||
switch (act) {
|
||||
case DVACT_QUIESCE:
|
||||
rv = config_activate_children(self, act);
|
||||
apple_platform_suspend(self);
|
||||
break;
|
||||
case DVACT_WAKEUP:
|
||||
apple_platform_resume(self);
|
||||
rv = config_activate_children(self, act);
|
||||
break;
|
||||
default:
|
||||
rv = config_activate_children(self, act);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
apldrm_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
|
||||
{
|
||||
struct rasops_info *ri = v;
|
||||
struct apldrm_softc *sc = ri->ri_hw;
|
||||
struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
|
||||
struct wsdisplay_fbinfo *wdf;
|
||||
struct backlight_device *bd;
|
||||
|
||||
bd = backlight_device_get_by_name("apple-panel-bl");
|
||||
|
||||
switch (cmd) {
|
||||
case WSDISPLAYIO_GTYPE:
|
||||
*(u_int *)data = WSDISPLAY_TYPE_KMS;
|
||||
return 0;
|
||||
case WSDISPLAYIO_GINFO:
|
||||
wdf = (struct wsdisplay_fbinfo *)data;
|
||||
wdf->width = ri->ri_width;
|
||||
wdf->height = ri->ri_height;
|
||||
wdf->depth = ri->ri_depth;
|
||||
wdf->stride = ri->ri_stride;
|
||||
wdf->offset = 0; /* XXX */
|
||||
wdf->cmsize = 0;
|
||||
return 0;
|
||||
case WSDISPLAYIO_GETPARAM:
|
||||
if (bd == NULL)
|
||||
return -1;
|
||||
|
||||
switch (dp->param) {
|
||||
case WSDISPLAYIO_PARAM_BRIGHTNESS:
|
||||
dp->min = 0;
|
||||
dp->max = bd->props.max_brightness;
|
||||
dp->curval = bd->props.brightness;
|
||||
return (dp->max > dp->min) ? 0 : -1;
|
||||
}
|
||||
break;
|
||||
case WSDISPLAYIO_SETPARAM:
|
||||
if (bd == NULL)
|
||||
return -1;
|
||||
|
||||
switch (dp->param) {
|
||||
case WSDISPLAYIO_PARAM_BRIGHTNESS:
|
||||
bd->props.brightness = dp->curval;
|
||||
backlight_update_status(bd);
|
||||
knote_locked(&sc->sc_ddev.note, NOTE_CHANGE);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case WSDISPLAYIO_SVIDEO:
|
||||
case WSDISPLAYIO_GVIDEO:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
paddr_t
|
||||
apldrm_wsmmap(void *v, off_t off, int prot)
|
||||
{
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
apldrm_alloc_screen(void *v, const struct wsscreen_descr *type,
|
||||
void **cookiep, int *curxp, int *curyp, uint32_t *attrp)
|
||||
{
|
||||
return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp);
|
||||
}
|
||||
|
||||
void
|
||||
apldrm_free_screen(void *v, void *cookie)
|
||||
{
|
||||
return rasops_free_screen(v, cookie);
|
||||
}
|
||||
|
||||
void
|
||||
apldrm_doswitch(void *v)
|
||||
{
|
||||
struct rasops_info *ri = v;
|
||||
struct apldrm_softc *sc = ri->ri_hw;
|
||||
struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper;
|
||||
|
||||
rasops_show_screen(ri, sc->sc_switchcookie, 0, NULL, NULL);
|
||||
drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
|
||||
|
||||
if (sc->sc_switchcb)
|
||||
(sc->sc_switchcb)(sc->sc_switchcbarg, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
apldrm_show_screen(void *v, void *cookie, int waitok,
|
||||
void (*cb)(void *, int, int), void *cbarg)
|
||||
{
|
||||
struct rasops_info *ri = v;
|
||||
struct apldrm_softc *sc = ri->ri_hw;
|
||||
|
||||
if (cookie == ri->ri_active)
|
||||
return (0);
|
||||
|
||||
sc->sc_switchcb = cb;
|
||||
sc->sc_switchcbarg = cbarg;
|
||||
sc->sc_switchcookie = cookie;
|
||||
if (cb) {
|
||||
task_add(systq, &sc->sc_switchtask);
|
||||
return (EAGAIN);
|
||||
}
|
||||
|
||||
apldrm_doswitch(v);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
apldrm_enter_ddb(void *v, void *cookie)
|
||||
{
|
||||
struct rasops_info *ri = v;
|
||||
struct apldrm_softc *sc = ri->ri_hw;
|
||||
struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper;
|
||||
|
||||
if (cookie == ri->ri_active)
|
||||
return;
|
||||
|
||||
rasops_show_screen(ri, cookie, 0, NULL, NULL);
|
||||
drm_fb_helper_debug_enter(fb_helper->info);
|
||||
}
|
||||
|
||||
void
|
||||
apldrm_burner(void *v, u_int on, u_int flags)
|
||||
{
|
||||
struct rasops_info *ri = v;
|
||||
struct apldrm_softc *sc = ri->ri_hw;
|
||||
|
||||
task_del(systq, &sc->sc_burner_task);
|
||||
|
||||
if (on)
|
||||
sc->sc_burner_fblank = FB_BLANK_UNBLANK;
|
||||
else {
|
||||
if (flags & WSDISPLAY_BURN_VBLANK)
|
||||
sc->sc_burner_fblank = FB_BLANK_VSYNC_SUSPEND;
|
||||
else
|
||||
sc->sc_burner_fblank = FB_BLANK_NORMAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setting the DPMS mode may sleep while waiting for vblank so
|
||||
* hand things off to a taskq.
|
||||
*/
|
||||
task_add(systq, &sc->sc_burner_task);
|
||||
}
|
||||
|
||||
void
|
||||
apldrm_burner_cb(void *arg)
|
||||
{
|
||||
struct apldrm_softc *sc = arg;
|
||||
struct drm_fb_helper *fb_helper = sc->sc_ddev.fb_helper;
|
||||
|
||||
drm_fb_helper_blank(sc->sc_burner_fblank, fb_helper->info);
|
||||
}
|
||||
|
||||
struct wsdisplay_accessops apldrm_accessops = {
|
||||
.ioctl = apldrm_wsioctl,
|
||||
.mmap = apldrm_wsmmap,
|
||||
.alloc_screen = apldrm_alloc_screen,
|
||||
.free_screen = apldrm_free_screen,
|
||||
.show_screen = apldrm_show_screen,
|
||||
.enter_ddb = apldrm_enter_ddb,
|
||||
.getchar = rasops_getchar,
|
||||
.load_font = rasops_load_font,
|
||||
.list_font = rasops_list_font,
|
||||
.scrollback = rasops_scrollback,
|
||||
.burn_screen = apldrm_burner
|
||||
};
|
||||
|
||||
void
|
||||
apldrm_attachhook(struct device *self)
|
||||
{
|
||||
struct apldrm_softc *sc = (struct apldrm_softc *)self;
|
||||
struct drm_fb_helper *fb_helper;
|
||||
struct rasops_info *ri = &sc->sc_ri;
|
||||
struct wsemuldisplaydev_attach_args waa;
|
||||
int console = 0;
|
||||
uint32_t defattr;
|
||||
int error;
|
||||
|
||||
error = apple_platform_probe(&sc->sc_dev);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
if (sc->sc_node == stdout_node)
|
||||
console = 1;
|
||||
|
||||
fb_helper = sc->sc_ddev.fb_helper;
|
||||
ri->ri_hw = sc;
|
||||
ri->ri_bits = fb_helper->info->screen_buffer;
|
||||
ri->ri_flg = RI_CENTER | RI_VCONS | RI_WRONLY;
|
||||
ri->ri_depth = fb_helper->fb->format->cpp[0] * 8;
|
||||
ri->ri_stride = fb_helper->fb->pitches[0];
|
||||
ri->ri_width = fb_helper->info->var.xres;
|
||||
ri->ri_height = fb_helper->info->var.yres;
|
||||
|
||||
switch (fb_helper->fb->format->format) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
ri->ri_rnum = 8;
|
||||
ri->ri_rpos = 16;
|
||||
ri->ri_gnum = 8;
|
||||
ri->ri_gpos = 8;
|
||||
ri->ri_bnum = 8;
|
||||
ri->ri_bpos = 0;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
ri->ri_rnum = 10;
|
||||
ri->ri_rpos = 20;
|
||||
ri->ri_gnum = 10;
|
||||
ri->ri_gpos = 10;
|
||||
ri->ri_bnum = 10;
|
||||
ri->ri_bpos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
rasops_init(ri, 160, 160);
|
||||
|
||||
strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name));
|
||||
sc->sc_wsd.capabilities = ri->ri_caps;
|
||||
sc->sc_wsd.nrows = ri->ri_rows;
|
||||
sc->sc_wsd.ncols = ri->ri_cols;
|
||||
sc->sc_wsd.textops = &ri->ri_ops;
|
||||
sc->sc_wsd.fontwidth = ri->ri_font->fontwidth;
|
||||
sc->sc_wsd.fontheight = ri->ri_font->fontheight;
|
||||
|
||||
sc->sc_scrlist[0] = &sc->sc_wsd;
|
||||
sc->sc_wsl.nscreens = 1;
|
||||
sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
|
||||
|
||||
task_set(&sc->sc_switchtask, apldrm_doswitch, ri);
|
||||
task_set(&sc->sc_burner_task, apldrm_burner_cb, sc);
|
||||
|
||||
if (console) {
|
||||
ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr);
|
||||
wsdisplay_cnattach(&sc->sc_wsd, ri->ri_active,
|
||||
ri->ri_ccol, ri->ri_crow, defattr);
|
||||
}
|
||||
|
||||
memset(&waa, 0, sizeof(waa));
|
||||
waa.scrdata = &sc->sc_wsl;
|
||||
waa.accessops = &apldrm_accessops;
|
||||
waa.accesscookie = ri;
|
||||
waa.console = console;
|
||||
|
||||
printf("%s: %dx%d, %dbpp\n", sc->sc_dev.dev.dv_xname,
|
||||
ri->ri_width, ri->ri_height, ri->ri_depth);
|
||||
|
||||
config_found_sm(self, &waa, wsemuldisplaydevprint,
|
||||
wsemuldisplaydevsubmatch);
|
||||
}
|
678
sys/dev/pci/drm/apple/apple_drv.c
Normal file
678
sys/dev/pci/drm/apple/apple_drv.c
Normal file
|
@ -0,0 +1,678 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
/* Based on meson driver which is
|
||||
* Copyright (C) 2016 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
|
||||
* Copyright (C) 2014 Endless Mobile
|
||||
*/
|
||||
|
||||
#include <linux/component.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <drm/drm_aperture.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_fbdev_dma.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_fb_dma_helper.h>
|
||||
#include <drm/drm_gem_dma_helper.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_mode.h>
|
||||
#include <drm/drm_modeset_helper.h>
|
||||
#include <drm/drm_module.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <drm/drm_fixed.h>
|
||||
|
||||
#include "dcp.h"
|
||||
|
||||
#define DRIVER_NAME "apple"
|
||||
#define DRIVER_DESC "Apple display controller DRM driver"
|
||||
|
||||
#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
|
||||
|
||||
#define MAX_COPROCESSORS 2
|
||||
|
||||
struct apple_drm_private {
|
||||
struct drm_device drm;
|
||||
};
|
||||
|
||||
DEFINE_DRM_GEM_DMA_FOPS(apple_fops);
|
||||
|
||||
#define DART_PAGE_SIZE 16384
|
||||
|
||||
static int apple_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *drm,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 64);
|
||||
args->size = round_up(args->pitch * args->height, DART_PAGE_SIZE);
|
||||
|
||||
return drm_gem_dma_dumb_create_internal(file_priv, drm, args);
|
||||
}
|
||||
|
||||
static const struct drm_driver apple_drm_driver = {
|
||||
DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(apple_drm_gem_dumb_create),
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = "20221106",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
|
||||
.fops = &apple_fops,
|
||||
};
|
||||
|
||||
static int apple_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_plane_state *new_plane_state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
|
||||
new_plane_state = drm_atomic_get_new_plane_state(state, plane);
|
||||
|
||||
if (!new_plane_state->crtc)
|
||||
return 0;
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
|
||||
if (IS_ERR(crtc_state))
|
||||
return PTR_ERR(crtc_state);
|
||||
|
||||
/*
|
||||
* DCP limits downscaling to 2x and upscaling to 4x. Attempting to
|
||||
* scale outside these bounds errors out when swapping.
|
||||
*
|
||||
* This function also takes care of clipping the src/dest rectangles,
|
||||
* which is required for correct operation. Partially off-screen
|
||||
* surfaces may appear corrupted.
|
||||
*
|
||||
* DCP does not distinguish plane types in the hardware, so we set
|
||||
* can_position. If the primary plane does not fill the screen, the
|
||||
* hardware will fill in zeroes (black).
|
||||
*/
|
||||
return drm_atomic_helper_check_plane_state(new_plane_state,
|
||||
crtc_state,
|
||||
FRAC_16_16(1, 4),
|
||||
FRAC_16_16(2, 1),
|
||||
true, true);
|
||||
}
|
||||
|
||||
static void apple_plane_atomic_update(struct drm_plane *plane,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
/* Handled in atomic_flush */
|
||||
}
|
||||
|
||||
static const struct drm_plane_helper_funcs apple_plane_helper_funcs = {
|
||||
.atomic_check = apple_plane_atomic_check,
|
||||
.atomic_update = apple_plane_atomic_update,
|
||||
};
|
||||
|
||||
static void apple_plane_cleanup(struct drm_plane *plane)
|
||||
{
|
||||
drm_plane_cleanup(plane);
|
||||
kfree(plane);
|
||||
}
|
||||
|
||||
static const struct drm_plane_funcs apple_plane_funcs = {
|
||||
.update_plane = drm_atomic_helper_update_plane,
|
||||
.disable_plane = drm_atomic_helper_disable_plane,
|
||||
.destroy = apple_plane_cleanup,
|
||||
.reset = drm_atomic_helper_plane_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
|
||||
};
|
||||
|
||||
/*
|
||||
* Table of supported formats, mapping from DRM fourccs to DCP fourccs.
|
||||
*
|
||||
* For future work, DCP supports more formats not listed, including YUV
|
||||
* formats, an extra RGBA format, and a biplanar RGB10_A8 format (fourcc b3a8)
|
||||
* used for HDR.
|
||||
*
|
||||
* Note: we don't have non-alpha formats but userspace breaks without XRGB. It
|
||||
* doesn't matter for the primary plane, but cursors/overlays must not
|
||||
* advertise formats without alpha.
|
||||
*/
|
||||
static const u32 dcp_formats[] = {
|
||||
DRM_FORMAT_XRGB2101010,
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_ARGB8888,
|
||||
DRM_FORMAT_XBGR8888,
|
||||
DRM_FORMAT_ABGR8888,
|
||||
};
|
||||
|
||||
u64 apple_format_modifiers[] = {
|
||||
DRM_FORMAT_MOD_LINEAR,
|
||||
DRM_FORMAT_MOD_INVALID
|
||||
};
|
||||
|
||||
static struct drm_plane *apple_plane_init(struct drm_device *dev,
|
||||
unsigned long possible_crtcs,
|
||||
enum drm_plane_type type)
|
||||
{
|
||||
int ret;
|
||||
struct drm_plane *plane;
|
||||
|
||||
plane = kzalloc(sizeof(*plane), GFP_KERNEL);
|
||||
|
||||
ret = drm_universal_plane_init(dev, plane, possible_crtcs,
|
||||
&apple_plane_funcs,
|
||||
dcp_formats, ARRAY_SIZE(dcp_formats),
|
||||
apple_format_modifiers, type, NULL);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
drm_plane_helper_add(plane, &apple_plane_helper_funcs);
|
||||
|
||||
return plane;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
apple_connector_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct apple_connector *apple_connector = to_apple_connector(connector);
|
||||
|
||||
return apple_connector->connected ? connector_status_connected :
|
||||
connector_status_disconnected;
|
||||
}
|
||||
|
||||
static void apple_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
|
||||
if (crtc_state->active_changed && crtc_state->active) {
|
||||
struct apple_crtc *apple_crtc = to_apple_crtc(crtc);
|
||||
dev_dbg(&apple_crtc->dcp->dev, "%s", __func__);
|
||||
dcp_poweron(apple_crtc->dcp);
|
||||
dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__);
|
||||
}
|
||||
|
||||
if (crtc_state->active)
|
||||
dcp_crtc_atomic_modeset(crtc, state);
|
||||
}
|
||||
|
||||
static void apple_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc_state *crtc_state;
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
|
||||
if (crtc_state->active_changed && !crtc_state->active) {
|
||||
struct apple_crtc *apple_crtc = to_apple_crtc(crtc);
|
||||
dev_dbg(&apple_crtc->dcp->dev, "%s", __func__);
|
||||
dcp_poweroff(apple_crtc->dcp);
|
||||
dev_dbg(&apple_crtc->dcp->dev, "%s finished", __func__);
|
||||
}
|
||||
|
||||
if (crtc->state->event && !crtc->state->active) {
|
||||
spin_lock_irq(&crtc->dev->event_lock);
|
||||
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
||||
spin_unlock_irq(&crtc->dev->event_lock);
|
||||
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void apple_crtc_atomic_begin(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct apple_crtc *apple_crtc = to_apple_crtc(crtc);
|
||||
unsigned long flags;
|
||||
|
||||
if (crtc->state->event) {
|
||||
spin_lock_irqsave(&crtc->dev->event_lock, flags);
|
||||
apple_crtc->event = crtc->state->event;
|
||||
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
|
||||
crtc->state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void dcp_atomic_commit_tail(struct drm_atomic_state *old_state)
|
||||
{
|
||||
struct drm_device *dev = old_state->dev;
|
||||
|
||||
drm_atomic_helper_commit_modeset_disables(dev, old_state);
|
||||
|
||||
drm_atomic_helper_commit_modeset_enables(dev, old_state);
|
||||
|
||||
drm_atomic_helper_commit_planes(dev, old_state,
|
||||
DRM_PLANE_COMMIT_ACTIVE_ONLY);
|
||||
|
||||
drm_atomic_helper_fake_vblank(old_state);
|
||||
|
||||
drm_atomic_helper_commit_hw_done(old_state);
|
||||
|
||||
drm_atomic_helper_wait_for_flip_done(dev, old_state);
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, old_state);
|
||||
}
|
||||
|
||||
static void apple_crtc_cleanup(struct drm_crtc *crtc)
|
||||
{
|
||||
drm_crtc_cleanup(crtc);
|
||||
kfree(to_apple_crtc(crtc));
|
||||
}
|
||||
|
||||
static const struct drm_crtc_funcs apple_crtc_funcs = {
|
||||
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
||||
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
||||
.destroy = apple_crtc_cleanup,
|
||||
.page_flip = drm_atomic_helper_page_flip,
|
||||
.reset = drm_atomic_helper_crtc_reset,
|
||||
.set_config = drm_atomic_helper_set_config,
|
||||
};
|
||||
|
||||
static const struct drm_mode_config_funcs apple_mode_config_funcs = {
|
||||
.atomic_check = drm_atomic_helper_check,
|
||||
.atomic_commit = drm_atomic_helper_commit,
|
||||
.fb_create = drm_gem_fb_create,
|
||||
};
|
||||
|
||||
static const struct drm_mode_config_helper_funcs apple_mode_config_helpers = {
|
||||
.atomic_commit_tail = dcp_atomic_commit_tail,
|
||||
};
|
||||
|
||||
static void appledrm_connector_cleanup(struct drm_connector *connector)
|
||||
{
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(to_apple_connector(connector));
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs apple_connector_funcs = {
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = appledrm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
.detect = apple_connector_detect,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs apple_connector_helper_funcs = {
|
||||
.get_modes = dcp_get_modes,
|
||||
.mode_valid = dcp_mode_valid,
|
||||
};
|
||||
|
||||
static const struct drm_crtc_helper_funcs apple_crtc_helper_funcs = {
|
||||
.atomic_begin = apple_crtc_atomic_begin,
|
||||
.atomic_check = dcp_crtc_atomic_check,
|
||||
.atomic_flush = dcp_flush,
|
||||
.atomic_enable = apple_crtc_atomic_enable,
|
||||
.atomic_disable = apple_crtc_atomic_disable,
|
||||
.mode_fixup = dcp_crtc_mode_fixup,
|
||||
};
|
||||
|
||||
static int apple_probe_per_dcp(struct device *dev,
|
||||
struct drm_device *drm,
|
||||
struct platform_device *dcp,
|
||||
int num, bool dcp_ext)
|
||||
{
|
||||
struct apple_crtc *crtc;
|
||||
struct apple_connector *connector;
|
||||
struct apple_encoder *enc;
|
||||
struct drm_plane *primary;
|
||||
int ret;
|
||||
|
||||
primary = apple_plane_init(drm, 1U << num, DRM_PLANE_TYPE_PRIMARY);
|
||||
|
||||
if (IS_ERR(primary))
|
||||
return PTR_ERR(primary);
|
||||
|
||||
crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
|
||||
ret = drm_crtc_init_with_planes(drm, &crtc->base, primary, NULL,
|
||||
&apple_crtc_funcs, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
drm_crtc_helper_add(&crtc->base, &apple_crtc_helper_funcs);
|
||||
drm_crtc_enable_color_mgmt(&crtc->base, 0, true, 0);
|
||||
|
||||
enc = drmm_simple_encoder_alloc(drm, struct apple_encoder, base,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
if (IS_ERR(enc))
|
||||
return PTR_ERR(enc);
|
||||
enc->base.possible_crtcs = drm_crtc_mask(&crtc->base);
|
||||
|
||||
connector = kzalloc(sizeof(*connector), GFP_KERNEL);
|
||||
drm_connector_helper_add(&connector->base,
|
||||
&apple_connector_helper_funcs);
|
||||
|
||||
#ifdef __linux__
|
||||
// HACK:
|
||||
if (dcp_ext)
|
||||
connector->base.fwnode = fwnode_handle_get(dev->fwnode);
|
||||
#endif
|
||||
|
||||
ret = drm_connector_init(drm, &connector->base, &apple_connector_funcs,
|
||||
dcp_get_connector_type(dcp));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
connector->base.polled = DRM_CONNECTOR_POLL_HPD;
|
||||
connector->connected = false;
|
||||
connector->dcp = dcp;
|
||||
|
||||
INIT_WORK(&connector->hotplug_wq, dcp_hotplug);
|
||||
|
||||
crtc->dcp = dcp;
|
||||
dcp_link(dcp, crtc, connector);
|
||||
|
||||
return drm_connector_attach_encoder(&connector->base, &enc->base);
|
||||
}
|
||||
|
||||
static int apple_get_fb_resource(struct device *dev, const char *name,
|
||||
struct resource *fb_r)
|
||||
{
|
||||
int idx, ret = -ENODEV;
|
||||
struct device_node *node;
|
||||
|
||||
idx = of_property_match_string(dev->of_node, "memory-region-names", name);
|
||||
|
||||
node = of_parse_phandle(dev->of_node, "memory-region", idx);
|
||||
if (!node) {
|
||||
dev_err(dev, "reserved-memory node '%s' not found\n", name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!of_device_is_available(node)) {
|
||||
dev_err(dev, "reserved-memory node '%s' is unavailable\n", name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!of_device_is_compatible(node, "framebuffer")) {
|
||||
dev_err(dev, "reserved-memory node '%s' is incompatible\n",
|
||||
node->full_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(node, 0, fb_r);
|
||||
|
||||
err:
|
||||
of_node_put(node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id apple_dcp_id_tbl[] = {
|
||||
{ .compatible = "apple,dcp" },
|
||||
{ .compatible = "apple,dcpext" },
|
||||
{},
|
||||
};
|
||||
|
||||
static int apple_drm_init_dcp(struct device *dev)
|
||||
{
|
||||
struct apple_drm_private *apple = dev_get_drvdata(dev);
|
||||
struct platform_device *dcp[MAX_COPROCESSORS];
|
||||
struct device_node *np;
|
||||
u64 timeout;
|
||||
int i, ret, num_dcp = 0;
|
||||
|
||||
for_each_matching_node(np, apple_dcp_id_tbl) {
|
||||
bool dcp_ext;
|
||||
if (!of_device_is_available(np)) {
|
||||
of_node_put(np);
|
||||
continue;
|
||||
}
|
||||
dcp_ext = of_device_is_compatible(np, "apple,dcpext");
|
||||
|
||||
dcp[num_dcp] = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
if (!dcp[num_dcp])
|
||||
continue;
|
||||
|
||||
ret = apple_probe_per_dcp(dev, &apple->drm, dcp[num_dcp],
|
||||
num_dcp, dcp_ext);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
ret = dcp_start(dcp[num_dcp]);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
num_dcp++;
|
||||
}
|
||||
|
||||
if (num_dcp < 1)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Starting DPTX might take some time.
|
||||
*/
|
||||
timeout = get_jiffies_64() + msecs_to_jiffies(3000);
|
||||
|
||||
for (i = 0; i < num_dcp; ++i) {
|
||||
u64 jiffies = get_jiffies_64();
|
||||
u64 wait = time_after_eq64(jiffies, timeout) ?
|
||||
0 :
|
||||
timeout - jiffies;
|
||||
ret = dcp_wait_ready(dcp[i], wait);
|
||||
/* There is nothing we can do if a dcp/dcpext does not boot
|
||||
* (successfully). Ignoring it should not do any harm now.
|
||||
* Needs to reevaluated whenn adding dcpext support.
|
||||
*/
|
||||
if (ret)
|
||||
dev_warn(dev, "DCP[%d] not ready: %d\n", i, ret);
|
||||
}
|
||||
/* HACK: Wait for dcp* to settle before a modeset */
|
||||
drm_msleep(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apple_drm_init(struct device *dev)
|
||||
{
|
||||
struct apple_drm_private *apple;
|
||||
struct resource fb_r;
|
||||
resource_size_t fb_size;
|
||||
int ret;
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(42));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = apple_get_fb_resource(dev, "framebuffer", &fb_r);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fb_size = fb_r.end - fb_r.start + 1;
|
||||
ret = drm_aperture_remove_conflicting_framebuffers(fb_r.start, fb_size,
|
||||
&apple_drm_driver);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed remove fb: %d\n", ret);
|
||||
goto err_unbind;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
apple = devm_drm_dev_alloc(dev, &apple_drm_driver,
|
||||
struct apple_drm_private, drm);
|
||||
if (IS_ERR(apple))
|
||||
return PTR_ERR(apple);
|
||||
#else
|
||||
struct apldrm_softc *sc = (struct apldrm_softc *)dev;
|
||||
apple = (struct apple_drm_private *)&sc->sc_ddev;
|
||||
#endif
|
||||
|
||||
dev_set_drvdata(dev, apple);
|
||||
|
||||
ret = component_bind_all(dev, apple);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drmm_mode_config_init(&apple->drm);
|
||||
if (ret)
|
||||
goto err_unbind;
|
||||
|
||||
/*
|
||||
* IOMFB::UPPipeDCP_H13P::verify_surfaces produces the error "plane
|
||||
* requires a minimum of 32x32 for the source buffer" if smaller
|
||||
*/
|
||||
apple->drm.mode_config.min_width = 32;
|
||||
apple->drm.mode_config.min_height = 32;
|
||||
|
||||
/*
|
||||
* TODO: this is the max framebuffer size not the maximal supported
|
||||
* output resolution. DCP reports the maximal framebuffer size take it
|
||||
* from there.
|
||||
* Hardcode it for now to the M1 Max DCP reported 'MaxSrcBufferWidth'
|
||||
* and 'MaxSrcBufferHeight' of 16384.
|
||||
*/
|
||||
apple->drm.mode_config.max_width = 16384;
|
||||
apple->drm.mode_config.max_height = 16384;
|
||||
|
||||
apple->drm.mode_config.funcs = &apple_mode_config_funcs;
|
||||
apple->drm.mode_config.helper_private = &apple_mode_config_helpers;
|
||||
|
||||
ret = apple_drm_init_dcp(dev);
|
||||
if (ret)
|
||||
goto err_unbind;
|
||||
|
||||
drm_mode_config_reset(&apple->drm);
|
||||
|
||||
ret = drm_dev_register(&apple->drm, 0);
|
||||
if (ret)
|
||||
goto err_unbind;
|
||||
|
||||
drm_fbdev_dma_setup(&apple->drm, 32);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unbind:
|
||||
component_unbind_all(dev, NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void apple_drm_uninit(struct device *dev)
|
||||
{
|
||||
struct apple_drm_private *apple = dev_get_drvdata(dev);
|
||||
|
||||
drm_dev_unregister(&apple->drm);
|
||||
drm_atomic_helper_shutdown(&apple->drm);
|
||||
|
||||
component_unbind_all(dev, NULL);
|
||||
|
||||
dev_set_drvdata(dev, NULL);
|
||||
}
|
||||
|
||||
static int apple_drm_bind(struct device *dev)
|
||||
{
|
||||
return apple_drm_init(dev);
|
||||
}
|
||||
|
||||
static void apple_drm_unbind(struct device *dev)
|
||||
{
|
||||
apple_drm_uninit(dev);
|
||||
}
|
||||
|
||||
const struct component_master_ops apple_drm_ops = {
|
||||
.bind = apple_drm_bind,
|
||||
.unbind = apple_drm_unbind,
|
||||
};
|
||||
|
||||
static int add_dcp_components(struct device *dev,
|
||||
struct component_match **matchptr)
|
||||
{
|
||||
struct device_node *np;
|
||||
int num = 0;
|
||||
|
||||
for_each_matching_node(np, apple_dcp_id_tbl) {
|
||||
if (of_device_is_available(np)) {
|
||||
drm_of_component_match_add(dev, matchptr,
|
||||
component_compare_of, np);
|
||||
num++;
|
||||
}
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static int apple_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *mdev = &pdev->dev;
|
||||
struct component_match *match = NULL;
|
||||
int num_dcp;
|
||||
|
||||
/* add DCP components, handle less than 1 as probe error */
|
||||
num_dcp = add_dcp_components(mdev, &match);
|
||||
if (num_dcp < 1)
|
||||
return -ENODEV;
|
||||
|
||||
return component_master_add_with_match(mdev, &apple_drm_ops, match);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static int apple_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_master_del(&pdev->dev, &apple_drm_ops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_match[] = {
|
||||
{ .compatible = "apple,display-subsystem" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_match);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int apple_platform_suspend(struct device *dev)
|
||||
{
|
||||
struct apple_drm_private *apple = dev_get_drvdata(dev);
|
||||
|
||||
if (apple)
|
||||
return drm_mode_config_helper_suspend(&apple->drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apple_platform_resume(struct device *dev)
|
||||
{
|
||||
struct apple_drm_private *apple = dev_get_drvdata(dev);
|
||||
|
||||
if (apple)
|
||||
drm_mode_config_helper_resume(&apple->drm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops apple_platform_pm_ops = {
|
||||
.suspend = apple_platform_suspend,
|
||||
.resume = apple_platform_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
static struct platform_driver apple_platform_driver = {
|
||||
.driver = {
|
||||
.name = "apple-drm",
|
||||
.of_match_table = of_match,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.pm = &apple_platform_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = apple_platform_probe,
|
||||
.remove = apple_platform_remove,
|
||||
};
|
||||
|
||||
drm_module_platform_driver(apple_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Alyssa Rosenzweig <alyssa@rosenzweig.io>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
|
||||
#endif
|
248
sys/dev/pci/drm/apple/dcp-internal.h
Normal file
248
sys/dev/pci/drm/apple/dcp-internal.h
Normal file
|
@ -0,0 +1,248 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
#ifndef __APPLE_DCP_INTERNAL_H__
|
||||
#define __APPLE_DCP_INTERNAL_H__
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/mux/consumer.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include "dptxep.h"
|
||||
#include "iomfb.h"
|
||||
#include "iomfb_v12_3.h"
|
||||
#include "iomfb_v13_3.h"
|
||||
|
||||
#define DCP_MAX_PLANES 2
|
||||
|
||||
struct apple_dcp;
|
||||
struct apple_dcp_afkep;
|
||||
|
||||
enum dcp_firmware_version {
|
||||
DCP_FIRMWARE_UNKNOWN,
|
||||
DCP_FIRMWARE_V_12_3,
|
||||
DCP_FIRMWARE_V_13_5,
|
||||
};
|
||||
|
||||
enum {
|
||||
SYSTEM_ENDPOINT = 0x20,
|
||||
TEST_ENDPOINT = 0x21,
|
||||
DCP_EXPERT_ENDPOINT = 0x22,
|
||||
DISP0_ENDPOINT = 0x23,
|
||||
DPTX_ENDPOINT = 0x2a,
|
||||
HDCP_ENDPOINT = 0x2b,
|
||||
REMOTE_ALLOC_ENDPOINT = 0x2d,
|
||||
IOMFB_ENDPOINT = 0x37,
|
||||
};
|
||||
|
||||
/* Temporary backing for a chunked transfer via setDCPAVPropStart/Chunk/End */
|
||||
struct dcp_chunks {
|
||||
size_t length;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#define DCP_MAX_MAPPINGS (128) /* should be enough */
|
||||
#define MAX_DISP_REGISTERS (7)
|
||||
|
||||
struct dcp_mem_descriptor {
|
||||
size_t size;
|
||||
void *buf;
|
||||
dma_addr_t dva;
|
||||
struct sg_table map;
|
||||
u64 reg;
|
||||
};
|
||||
|
||||
/* Limit on call stack depth (arbitrary). Some nesting is required */
|
||||
#define DCP_MAX_CALL_DEPTH 8
|
||||
|
||||
typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *);
|
||||
|
||||
struct dcp_channel {
|
||||
dcp_callback_t callbacks[DCP_MAX_CALL_DEPTH];
|
||||
void *cookies[DCP_MAX_CALL_DEPTH];
|
||||
void *output[DCP_MAX_CALL_DEPTH];
|
||||
u16 end[DCP_MAX_CALL_DEPTH];
|
||||
|
||||
/* Current depth of the call stack. Less than DCP_MAX_CALL_DEPTH */
|
||||
u8 depth;
|
||||
};
|
||||
|
||||
struct dcp_fb_reference {
|
||||
struct list_head head;
|
||||
struct drm_framebuffer *fb;
|
||||
u32 swap_id;
|
||||
};
|
||||
|
||||
#define MAX_NOTCH_HEIGHT 160
|
||||
|
||||
struct dcp_brightness {
|
||||
struct backlight_device *bl_dev;
|
||||
u32 maximum;
|
||||
u32 dac;
|
||||
int nits;
|
||||
int scale;
|
||||
bool update;
|
||||
};
|
||||
|
||||
/** laptop/AiO integrated panel parameters from DT */
|
||||
struct dcp_panel {
|
||||
/// panel width in millimeter
|
||||
int width_mm;
|
||||
/// panel height in millimeter
|
||||
int height_mm;
|
||||
/// panel has a mini-LED backllight
|
||||
bool has_mini_led;
|
||||
};
|
||||
|
||||
struct apple_dcp_hw_data {
|
||||
u32 num_dptx_ports;
|
||||
};
|
||||
|
||||
/* TODO: move IOMFB members to its own struct */
|
||||
struct apple_dcp {
|
||||
struct device *dev;
|
||||
struct platform_device *piodma;
|
||||
struct iommu_domain *iommu_dom;
|
||||
struct apple_rtkit *rtk;
|
||||
struct apple_crtc *crtc;
|
||||
struct apple_connector *connector;
|
||||
|
||||
struct apple_dcp_hw_data hw;
|
||||
|
||||
/* firmware version and compatible firmware version */
|
||||
enum dcp_firmware_version fw_compat;
|
||||
|
||||
/* Coprocessor control register */
|
||||
void __iomem *coproc_reg;
|
||||
|
||||
/* DCP has crashed */
|
||||
bool crashed;
|
||||
|
||||
/************* IOMFB **************************************************
|
||||
* everything below is mostly used inside IOMFB but it could make *
|
||||
* sense keep some of the the members in apple_dcp. *
|
||||
**********************************************************************/
|
||||
|
||||
/* clock rate request by dcp in */
|
||||
struct clk *clk;
|
||||
|
||||
/* DCP shared memory */
|
||||
void *shmem;
|
||||
|
||||
/* Display registers mappable to the DCP */
|
||||
struct resource *disp_registers[MAX_DISP_REGISTERS];
|
||||
unsigned int nr_disp_registers;
|
||||
|
||||
struct resource disp_bw_scratch_res;
|
||||
struct resource disp_bw_doorbell_res;
|
||||
u32 disp_bw_scratch_index;
|
||||
u32 disp_bw_scratch_offset;
|
||||
u32 disp_bw_doorbell_index;
|
||||
u32 disp_bw_doorbell_offset;
|
||||
|
||||
u32 index;
|
||||
|
||||
/* Bitmap of memory descriptors used for mappings made by the DCP */
|
||||
DECLARE_BITMAP(memdesc_map, DCP_MAX_MAPPINGS);
|
||||
|
||||
/* Indexed table of memory descriptors */
|
||||
struct dcp_mem_descriptor memdesc[DCP_MAX_MAPPINGS];
|
||||
|
||||
struct dcp_channel ch_cmd, ch_oobcmd;
|
||||
struct dcp_channel ch_cb, ch_oobcb, ch_async, ch_oobasync;
|
||||
|
||||
/* iomfb EP callback handlers */
|
||||
const iomfb_cb_handler *cb_handlers;
|
||||
|
||||
/* Active chunked transfer. There can only be one at a time. */
|
||||
struct dcp_chunks chunks;
|
||||
|
||||
/* Queued swap. Owned by the DCP to avoid per-swap memory allocation */
|
||||
union {
|
||||
struct dcp_swap_submit_req_v12_3 v12_3;
|
||||
struct dcp_swap_submit_req_v13_3 v13_3;
|
||||
} swap;
|
||||
|
||||
/* swap id of the last completed swap */
|
||||
u32 last_swap_id;
|
||||
|
||||
/* Current display mode */
|
||||
bool during_modeset;
|
||||
bool valid_mode;
|
||||
struct dcp_set_digital_out_mode_req mode;
|
||||
|
||||
/* completion for active turning true */
|
||||
struct completion start_done;
|
||||
|
||||
/* Is the DCP booted? */
|
||||
bool active;
|
||||
|
||||
/* eDP display without DP-HDMI conversion */
|
||||
bool main_display;
|
||||
|
||||
/* clear all surfaces on init */
|
||||
bool surfaces_cleared;
|
||||
|
||||
/* Modes valid for the connected display */
|
||||
struct dcp_display_mode *modes;
|
||||
unsigned int nr_modes;
|
||||
|
||||
/* Attributes of the connector */
|
||||
int connector_type;
|
||||
|
||||
/* Attributes of the connected display */
|
||||
int width_mm, height_mm;
|
||||
|
||||
unsigned notch_height;
|
||||
|
||||
/* Workqueue for sending vblank events when a dcp swap is not possible */
|
||||
struct work_struct vblank_wq;
|
||||
|
||||
/* List of referenced drm_framebuffers which can be unreferenced
|
||||
* on the next successfully completed swap.
|
||||
*/
|
||||
struct list_head swapped_out_fbs;
|
||||
|
||||
struct dcp_brightness brightness;
|
||||
/* Workqueue for updating the initial initial brightness */
|
||||
struct work_struct bl_register_wq;
|
||||
struct rwlock bl_register_mutex;
|
||||
|
||||
/* integrated panel if present */
|
||||
struct dcp_panel panel;
|
||||
|
||||
struct apple_dcp_afkep *systemep;
|
||||
struct completion systemep_done;
|
||||
|
||||
struct apple_dcp_afkep *ibootep;
|
||||
|
||||
struct apple_dcp_afkep *dptxep;
|
||||
|
||||
struct dptx_port dptxport[2];
|
||||
|
||||
/* these fields are output port specific */
|
||||
struct phy *phy;
|
||||
struct mux_control *xbar;
|
||||
|
||||
struct gpio_desc *hdmi_hpd;
|
||||
struct gpio_desc *hdmi_pwren;
|
||||
struct gpio_desc *dp2hdmi_pwren;
|
||||
|
||||
struct rwlock hpd_mutex;
|
||||
|
||||
u32 dptx_phy;
|
||||
u32 dptx_die;
|
||||
int hdmi_hpd_irq;
|
||||
};
|
||||
|
||||
int dcp_backlight_register(struct apple_dcp *dcp);
|
||||
bool dcp_has_panel(struct apple_dcp *dcp);
|
||||
|
||||
#define DCP_AUDIO_MAX_CHANS 15
|
||||
|
||||
#endif /* __APPLE_DCP_INTERNAL_H__ */
|
1115
sys/dev/pci/drm/apple/dcp.c
Normal file
1115
sys/dev/pci/drm/apple/dcp.c
Normal file
File diff suppressed because it is too large
Load diff
76
sys/dev/pci/drm/apple/dcp.h
Normal file
76
sys/dev/pci/drm/apple/dcp.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
#ifndef __APPLE_DCP_H__
|
||||
#define __APPLE_DCP_H__
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
|
||||
#include "dcp-internal.h"
|
||||
#include "parser.h"
|
||||
|
||||
struct apple_crtc {
|
||||
struct drm_crtc base;
|
||||
struct drm_pending_vblank_event *event;
|
||||
bool vsync_disabled;
|
||||
|
||||
/* Reference to the DCP device owning this CRTC */
|
||||
struct platform_device *dcp;
|
||||
};
|
||||
|
||||
#define to_apple_crtc(x) container_of(x, struct apple_crtc, base)
|
||||
|
||||
void dcp_hotplug(struct work_struct *work);
|
||||
|
||||
struct apple_connector {
|
||||
struct drm_connector base;
|
||||
bool connected;
|
||||
|
||||
struct platform_device *dcp;
|
||||
|
||||
/* Workqueue for sending hotplug events to the associated device */
|
||||
struct work_struct hotplug_wq;
|
||||
};
|
||||
|
||||
#define to_apple_connector(x) container_of(x, struct apple_connector, base)
|
||||
|
||||
struct apple_encoder {
|
||||
struct drm_encoder base;
|
||||
};
|
||||
|
||||
#define to_apple_encoder(x) container_of(x, struct apple_encoder, base)
|
||||
|
||||
void dcp_poweroff(struct platform_device *pdev);
|
||||
void dcp_poweron(struct platform_device *pdev);
|
||||
int dcp_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state);
|
||||
int dcp_get_connector_type(struct platform_device *pdev);
|
||||
void dcp_link(struct platform_device *pdev, struct apple_crtc *apple,
|
||||
struct apple_connector *connector);
|
||||
int dcp_start(struct platform_device *pdev);
|
||||
int dcp_wait_ready(struct platform_device *pdev, u64 timeout);
|
||||
void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state);
|
||||
bool dcp_is_initialized(struct platform_device *pdev);
|
||||
void apple_crtc_vblank(struct apple_crtc *apple);
|
||||
void dcp_drm_crtc_vblank(struct apple_crtc *crtc);
|
||||
int dcp_get_modes(struct drm_connector *connector);
|
||||
int dcp_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode);
|
||||
int dcp_crtc_atomic_modeset(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state);
|
||||
bool dcp_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
void dcp_set_dimensions(struct apple_dcp *dcp);
|
||||
void dcp_send_message(struct apple_dcp *dcp, u8 endpoint, u64 message);
|
||||
|
||||
int iomfb_start_rtkit(struct apple_dcp *dcp);
|
||||
void iomfb_shutdown(struct apple_dcp *dcp);
|
||||
/* rtkit message handler for IOMFB messages */
|
||||
void iomfb_recv_msg(struct apple_dcp *dcp, u64 message);
|
||||
|
||||
int systemep_init(struct apple_dcp *dcp);
|
||||
int dptxep_init(struct apple_dcp *dcp);
|
||||
int ibootep_init(struct apple_dcp *dcp);
|
||||
#endif
|
231
sys/dev/pci/drm/apple/dcp_backlight.c
Normal file
231
sys/dev/pci/drm/apple/dcp_backlight.c
Normal file
|
@ -0,0 +1,231 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright (C) The Asahi Linux Contributors */
|
||||
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_modeset_lock.h>
|
||||
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include "linux/jiffies.h"
|
||||
|
||||
#include "dcp.h"
|
||||
#include "dcp-internal.h"
|
||||
|
||||
#define MIN_BRIGHTNESS_PART1 2U
|
||||
#define MAX_BRIGHTNESS_PART1 99U
|
||||
#define MIN_BRIGHTNESS_PART2 103U
|
||||
#define MAX_BRIGHTNESS_PART2 510U
|
||||
|
||||
/*
|
||||
* lookup for display brightness 2 to 99 nits
|
||||
* */
|
||||
static u32 brightness_part1[] = {
|
||||
0x0000000, 0x0810038, 0x0f000bd, 0x143011c,
|
||||
0x1850165, 0x1bc01a1, 0x1eb01d4, 0x2140200,
|
||||
0x2380227, 0x2590249, 0x2770269, 0x2930285,
|
||||
0x2ac02a0, 0x2c402b8, 0x2d902cf, 0x2ee02e4,
|
||||
0x30102f8, 0x314030b, 0x325031c, 0x335032d,
|
||||
0x345033d, 0x354034d, 0x362035b, 0x3700369,
|
||||
0x37d0377, 0x38a0384, 0x3960390, 0x3a2039c,
|
||||
0x3ad03a7, 0x3b803b3, 0x3c303bd, 0x3cd03c8,
|
||||
0x3d703d2, 0x3e103dc, 0x3ea03e5, 0x3f303ef,
|
||||
0x3fc03f8, 0x4050400, 0x40d0409, 0x4150411,
|
||||
0x41d0419, 0x4250421, 0x42d0429, 0x4340431,
|
||||
0x43c0438, 0x443043f, 0x44a0446, 0x451044d,
|
||||
0x4570454, 0x45e045b, 0x4640461, 0x46b0468,
|
||||
0x471046e, 0x4770474, 0x47d047a, 0x4830480,
|
||||
0x4890486, 0x48e048b, 0x4940491, 0x4990497,
|
||||
0x49f049c, 0x4a404a1, 0x4a904a7, 0x4ae04ac,
|
||||
0x4b304b1, 0x4b804b6, 0x4bd04bb, 0x4c204c0,
|
||||
0x4c704c5, 0x4cc04c9, 0x4d004ce, 0x4d504d3,
|
||||
0x4d904d7, 0x4de04dc, 0x4e204e0, 0x4e704e4,
|
||||
0x4eb04e9, 0x4ef04ed, 0x4f304f1, 0x4f704f5,
|
||||
0x4fb04f9, 0x4ff04fd, 0x5030501, 0x5070505,
|
||||
0x50b0509, 0x50f050d, 0x5130511, 0x5160515,
|
||||
0x51a0518, 0x51e051c, 0x5210520, 0x5250523,
|
||||
0x5290527, 0x52c052a, 0x52f052e, 0x5330531,
|
||||
0x5360535, 0x53a0538, 0x53d053b, 0x540053f,
|
||||
0x5440542, 0x5470545, 0x54a0548, 0x54d054c,
|
||||
0x550054f, 0x5530552, 0x5560555, 0x5590558,
|
||||
0x55c055b, 0x55f055e, 0x5620561, 0x5650564,
|
||||
0x5680567, 0x56b056a, 0x56e056d, 0x571056f,
|
||||
0x5740572, 0x5760575, 0x5790578, 0x57c057b,
|
||||
0x57f057d, 0x5810580, 0x5840583, 0x5870585,
|
||||
0x5890588, 0x58c058b, 0x58f058d
|
||||
};
|
||||
|
||||
static u32 brightness_part12[] = { 0x58f058d, 0x59d058f };
|
||||
|
||||
/*
|
||||
* lookup table for display brightness 103.3 to 510 nits
|
||||
* */
|
||||
static u32 brightness_part2[] = {
|
||||
0x59d058f, 0x5b805ab, 0x5d105c5, 0x5e805dd,
|
||||
0x5fe05f3, 0x6120608, 0x625061c, 0x637062e,
|
||||
0x6480640, 0x6580650, 0x6680660, 0x677066f,
|
||||
0x685067e, 0x693068c, 0x6a00699, 0x6ac06a6,
|
||||
0x6b806b2, 0x6c406be, 0x6cf06ca, 0x6da06d5,
|
||||
0x6e506df, 0x6ef06ea, 0x6f906f4, 0x70206fe,
|
||||
0x70c0707, 0x7150710, 0x71e0719, 0x7260722,
|
||||
0x72f072a, 0x7370733, 0x73f073b, 0x7470743,
|
||||
0x74e074a, 0x7560752, 0x75d0759, 0x7640760,
|
||||
0x76b0768, 0x772076e, 0x7780775, 0x77f077c,
|
||||
0x7850782, 0x78c0789, 0x792078f, 0x7980795,
|
||||
0x79e079b, 0x7a407a1, 0x7aa07a7, 0x7af07ac,
|
||||
0x7b507b2, 0x7ba07b8, 0x7c007bd, 0x7c507c2,
|
||||
0x7ca07c8, 0x7cf07cd, 0x7d407d2, 0x7d907d7,
|
||||
0x7de07dc, 0x7e307e1, 0x7e807e5, 0x7ec07ea,
|
||||
0x7f107ef, 0x7f607f3, 0x7fa07f8, 0x7fe07fc
|
||||
};
|
||||
|
||||
|
||||
static int dcp_get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct apple_dcp *dcp = bl_get_data(bd);
|
||||
|
||||
return dcp->brightness.nits;
|
||||
}
|
||||
|
||||
#define SCALE_FACTOR (1 << 10)
|
||||
|
||||
static u32 interpolate(int val, int min, int max, u32 *tbl, size_t tbl_size)
|
||||
{
|
||||
u32 frac;
|
||||
u64 low, high;
|
||||
u32 interpolated = (tbl_size - 1) * ((val - min) * SCALE_FACTOR) / (max - min);
|
||||
|
||||
size_t index = interpolated / SCALE_FACTOR;
|
||||
|
||||
if (WARN(index + 1 >= tbl_size, "invalid index %zu for brightness %u", index, val))
|
||||
return tbl[tbl_size / 2];
|
||||
|
||||
frac = interpolated & (SCALE_FACTOR - 1);
|
||||
low = tbl[index];
|
||||
high = tbl[index + 1];
|
||||
|
||||
return ((frac * high) + ((SCALE_FACTOR - frac) * low)) / SCALE_FACTOR;
|
||||
}
|
||||
|
||||
static u32 calculate_dac(struct apple_dcp *dcp, int val)
|
||||
{
|
||||
u32 dac;
|
||||
|
||||
if (val <= MIN_BRIGHTNESS_PART1)
|
||||
return 16 * brightness_part1[0];
|
||||
else if (val == MAX_BRIGHTNESS_PART1)
|
||||
return 16 * brightness_part1[ARRAY_SIZE(brightness_part1) - 1];
|
||||
else if (val == MIN_BRIGHTNESS_PART2)
|
||||
return 16 * brightness_part2[0];
|
||||
else if (val >= MAX_BRIGHTNESS_PART2)
|
||||
return brightness_part2[ARRAY_SIZE(brightness_part2) - 1];
|
||||
|
||||
if (val < MAX_BRIGHTNESS_PART1) {
|
||||
dac = interpolate(val, MIN_BRIGHTNESS_PART1, MAX_BRIGHTNESS_PART1,
|
||||
brightness_part1, ARRAY_SIZE(brightness_part1));
|
||||
} else if (val > MIN_BRIGHTNESS_PART2) {
|
||||
dac = interpolate(val, MIN_BRIGHTNESS_PART2, MAX_BRIGHTNESS_PART2,
|
||||
brightness_part2, ARRAY_SIZE(brightness_part2));
|
||||
} else {
|
||||
dac = interpolate(val, MAX_BRIGHTNESS_PART1, MIN_BRIGHTNESS_PART2,
|
||||
brightness_part12, ARRAY_SIZE(brightness_part12));
|
||||
}
|
||||
|
||||
return 16 * dac;
|
||||
}
|
||||
|
||||
static int drm_crtc_set_brightness(struct apple_dcp *dcp)
|
||||
{
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_crtc *crtc = &dcp->crtc->base;
|
||||
int ret = 0;
|
||||
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(crtc->dev, ctx, 0, ret);
|
||||
|
||||
if (!dcp->brightness.update)
|
||||
goto done;
|
||||
|
||||
state = drm_atomic_state_alloc(crtc->dev);
|
||||
if (!state)
|
||||
return -ENOMEM;
|
||||
|
||||
state->acquire_ctx = &ctx;
|
||||
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
||||
if (IS_ERR(crtc_state)) {
|
||||
ret = PTR_ERR(crtc_state);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
crtc_state->color_mgmt_changed |= true;
|
||||
|
||||
ret = drm_atomic_commit(state);
|
||||
|
||||
fail:
|
||||
drm_atomic_state_put(state);
|
||||
done:
|
||||
DRM_MODESET_LOCK_ALL_END(crtc->dev, ctx, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dcp_set_brightness(struct backlight_device *bd)
|
||||
{
|
||||
int ret = 0;
|
||||
struct apple_dcp *dcp = bl_get_data(bd);
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
int brightness = backlight_get_brightness(bd);
|
||||
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(dcp->crtc->base.dev, ctx, 0, ret);
|
||||
|
||||
dcp->brightness.dac = calculate_dac(dcp, brightness);
|
||||
dcp->brightness.update = true;
|
||||
|
||||
DRM_MODESET_LOCK_ALL_END(dcp->crtc->base.dev, ctx, ret);
|
||||
|
||||
/*
|
||||
* Do not actively try to change brightness if no mode is set.
|
||||
* TODO: should this be reflected the in backlight's power property?
|
||||
* defer this hopefully until it becomes irrelevant due to proper
|
||||
* drm integrated backlight handling
|
||||
*/
|
||||
if (!dcp->valid_mode)
|
||||
return 0;
|
||||
|
||||
/* Wait 1 vblank cycle in the hope an atomic swap has already updated
|
||||
* the brightness */
|
||||
drm_msleep((1001 + 23) / 24); // 42ms for 23.976 fps
|
||||
|
||||
return drm_crtc_set_brightness(dcp);
|
||||
}
|
||||
|
||||
static const struct backlight_ops dcp_backlight_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.get_brightness = dcp_get_brightness,
|
||||
.update_status = dcp_set_brightness,
|
||||
};
|
||||
|
||||
int dcp_backlight_register(struct apple_dcp *dcp)
|
||||
{
|
||||
struct device *dev = dcp->dev;
|
||||
struct backlight_device *bl_dev;
|
||||
struct backlight_properties props = {
|
||||
.type = BACKLIGHT_PLATFORM,
|
||||
.brightness = dcp->brightness.nits,
|
||||
.scale = BACKLIGHT_SCALE_LINEAR,
|
||||
};
|
||||
props.max_brightness = min(dcp->brightness.maximum, MAX_BRIGHTNESS_PART2 - 1);
|
||||
|
||||
bl_dev = devm_backlight_device_register(dev, "apple-panel-bl", dev, dcp,
|
||||
&dcp_backlight_ops, &props);
|
||||
if (IS_ERR(bl_dev))
|
||||
return PTR_ERR(bl_dev);
|
||||
|
||||
dcp->brightness.bl_dev = bl_dev;
|
||||
dcp->brightness.dac = calculate_dac(dcp, dcp->brightness.nits);
|
||||
|
||||
return 0;
|
||||
}
|
604
sys/dev/pci/drm/apple/dptxep.c
Normal file
604
sys/dev/pci/drm/apple/dptxep.c
Normal file
|
@ -0,0 +1,604 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "afk.h"
|
||||
#include "dcp.h"
|
||||
#include "dptxep.h"
|
||||
#include "parser.h"
|
||||
#include "trace.h"
|
||||
|
||||
struct dcpdptx_connection_cmd {
|
||||
__le32 unk;
|
||||
__le32 target;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dcpdptx_hotplug_cmd {
|
||||
u8 _pad0[16];
|
||||
__le32 unk;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dptxport_apcall_link_rate {
|
||||
__le32 retcode;
|
||||
u8 _unk0[12];
|
||||
__le32 link_rate;
|
||||
u8 _unk1[12];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dptxport_apcall_lane_count {
|
||||
__le32 retcode;
|
||||
u8 _unk0[12];
|
||||
__le64 lane_count;
|
||||
u8 _unk1[8];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dptxport_apcall_set_active_lane_count {
|
||||
__le32 retcode;
|
||||
u8 _unk0[12];
|
||||
__le64 lane_count;
|
||||
u8 _unk1[8];
|
||||
} __packed;
|
||||
|
||||
struct dptxport_apcall_get_support {
|
||||
__le32 retcode;
|
||||
u8 _unk0[12];
|
||||
__le32 supported;
|
||||
u8 _unk1[12];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dptxport_apcall_max_drive_settings {
|
||||
__le32 retcode;
|
||||
u8 _unk0[12];
|
||||
__le32 max_drive_settings[2];
|
||||
u8 _unk1[8];
|
||||
};
|
||||
|
||||
struct dptxport_apcall_drive_settings {
|
||||
__le32 retcode;
|
||||
u8 _unk0[12];
|
||||
__le32 unk1;
|
||||
__le32 unk2;
|
||||
__le32 unk3;
|
||||
__le32 unk4;
|
||||
__le32 unk5;
|
||||
__le32 unk6;
|
||||
__le32 unk7;
|
||||
};
|
||||
|
||||
int dptxport_validate_connection(struct apple_epic_service *service, u8 core,
|
||||
u8 atc, u8 die)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
struct dcpdptx_connection_cmd cmd, resp;
|
||||
int ret;
|
||||
u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_ATC, atc) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) |
|
||||
DCPDPTX_REMOTE_PORT_CONNECTED;
|
||||
|
||||
trace_dptxport_validate_connection(dptx, core, atc, die);
|
||||
|
||||
cmd.target = cpu_to_le32(target);
|
||||
cmd.unk = cpu_to_le32(0x100);
|
||||
ret = afk_service_call(service, 0, 12, &cmd, sizeof(cmd), 40, &resp,
|
||||
sizeof(resp), 40);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (le32_to_cpu(resp.target) != target)
|
||||
return -EINVAL;
|
||||
if (le32_to_cpu(resp.unk) != 0x100)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc,
|
||||
u8 die)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
struct dcpdptx_connection_cmd cmd, resp;
|
||||
u32 unk_field = 0x0; // seen as 0x100 under some conditions
|
||||
int ret;
|
||||
u32 target = FIELD_PREP(DCPDPTX_REMOTE_PORT_CORE, core) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_ATC, atc) |
|
||||
FIELD_PREP(DCPDPTX_REMOTE_PORT_DIE, die) |
|
||||
DCPDPTX_REMOTE_PORT_CONNECTED;
|
||||
|
||||
trace_dptxport_connect(dptx, core, atc, die);
|
||||
|
||||
cmd.target = cpu_to_le32(target);
|
||||
cmd.unk = cpu_to_le32(unk_field);
|
||||
ret = afk_service_call(service, 0, 11, &cmd, sizeof(cmd), 24, &resp,
|
||||
sizeof(resp), 24);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (le32_to_cpu(resp.target) != target)
|
||||
return -EINVAL;
|
||||
if (le32_to_cpu(resp.unk) != unk_field)
|
||||
dev_notice(service->ep->dcp->dev, "unexpected unk field in reply: 0x%x (0x%x)\n",
|
||||
le32_to_cpu(resp.unk), unk_field);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dptxport_request_display(struct apple_epic_service *service)
|
||||
{
|
||||
return afk_service_call(service, 0, 6, NULL, 0, 16, NULL, 0, 16);
|
||||
}
|
||||
|
||||
int dptxport_release_display(struct apple_epic_service *service)
|
||||
{
|
||||
return afk_service_call(service, 0, 7, NULL, 0, 16, NULL, 0, 16);
|
||||
}
|
||||
|
||||
int dptxport_set_hpd(struct apple_epic_service *service, bool hpd)
|
||||
{
|
||||
struct dcpdptx_hotplug_cmd cmd, resp;
|
||||
int ret;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
|
||||
if (hpd)
|
||||
cmd.unk = cpu_to_le32(1);
|
||||
|
||||
ret = afk_service_call(service, 8, 8, &cmd, sizeof(cmd), 12, &resp,
|
||||
sizeof(resp), 12);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (le32_to_cpu(resp.unk) != 1)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_get_max_drive_settings(struct apple_epic_service *service,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_max_drive_settings *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -EINVAL;
|
||||
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
reply->max_drive_settings[0] = cpu_to_le32(0x3);
|
||||
reply->max_drive_settings[1] = cpu_to_le32(0x3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_get_drive_settings(struct apple_epic_service *service,
|
||||
const void *request_, size_t request_size,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
const struct dptxport_apcall_drive_settings *request = request_;
|
||||
struct dptxport_apcall_drive_settings *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply) || request_size < sizeof(*request))
|
||||
return -EINVAL;
|
||||
|
||||
*reply = *request;
|
||||
|
||||
/* Clear the rest of the buffer */
|
||||
memset(reply_ + sizeof(*reply), 0, reply_size - sizeof(*reply));
|
||||
|
||||
if (reply->retcode != 4)
|
||||
dev_err(service->ep->dcp->dev,
|
||||
"get_drive_settings: unexpected retcode %d\n",
|
||||
reply->retcode);
|
||||
|
||||
reply->retcode = 4; /* Should already be 4? */
|
||||
reply->unk5 = dptx->drive_settings[0];
|
||||
reply->unk6 = 0;
|
||||
reply->unk7 = dptx->drive_settings[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_set_drive_settings(struct apple_epic_service *service,
|
||||
const void *request_, size_t request_size,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
const struct dptxport_apcall_drive_settings *request = request_;
|
||||
struct dptxport_apcall_drive_settings *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply) || request_size < sizeof(*request))
|
||||
return -EINVAL;
|
||||
|
||||
*reply = *request;
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
|
||||
dev_info(service->ep->dcp->dev, "set_drive_settings: %d:%d:%d:%d:%d:%d:%d\n",
|
||||
request->unk1, request->unk2, request->unk3, request->unk4,
|
||||
request->unk5, request->unk6, request->unk7);
|
||||
|
||||
dptx->drive_settings[0] = reply->unk5;
|
||||
dptx->drive_settings[1] = reply->unk7;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_max_link_rate(struct apple_epic_service *service,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_link_rate *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -EINVAL;
|
||||
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
reply->link_rate = cpu_to_le32(LINK_RATE_HBR3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_max_lane_count(struct apple_epic_service *service,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_lane_count *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -EINVAL;
|
||||
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
reply->lane_count = cpu_to_le64(4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_set_active_lane_count(struct apple_epic_service *service,
|
||||
const void *data, size_t data_size,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
const struct dptxport_apcall_set_active_lane_count *request = data;
|
||||
struct dptxport_apcall_set_active_lane_count *reply = reply_;
|
||||
int ret = 0;
|
||||
int retcode = 0;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -1;
|
||||
if (data_size < sizeof(*request))
|
||||
return -1;
|
||||
|
||||
u64 lane_count = cpu_to_le64(request->lane_count);
|
||||
|
||||
switch (lane_count) {
|
||||
case 0 ... 2:
|
||||
case 4:
|
||||
dptx->phy_ops.dp.lanes = lane_count;
|
||||
dptx->phy_ops.dp.set_lanes = 1;
|
||||
break;
|
||||
default:
|
||||
dev_err(service->ep->dcp->dev, "set_active_lane_count: invalid lane count:%llu\n", lane_count);
|
||||
retcode = 1;
|
||||
lane_count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dptx->phy_ops.dp.set_lanes) {
|
||||
if (dptx->atcphy) {
|
||||
ret = phy_configure(dptx->atcphy, &dptx->phy_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
dptx->phy_ops.dp.set_lanes = 0;
|
||||
}
|
||||
|
||||
dptx->lane_count = lane_count;
|
||||
|
||||
reply->retcode = cpu_to_le32(retcode);
|
||||
reply->lane_count = cpu_to_le64(lane_count);
|
||||
|
||||
if (dptx->lane_count > 0)
|
||||
complete(&dptx->linkcfg_completion);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_link_rate(struct apple_epic_service *service,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
struct dptxport_apcall_link_rate *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -EINVAL;
|
||||
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
reply->link_rate = cpu_to_le32(dptx->link_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_will_change_link_config(struct apple_epic_service *service)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
|
||||
dptx->phy_ops.dp.set_lanes = 0;
|
||||
dptx->phy_ops.dp.set_rate = 0;
|
||||
dptx->phy_ops.dp.set_voltages = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_did_change_link_config(struct apple_epic_service *service)
|
||||
{
|
||||
/* assume the link config did change and wait a little bit */
|
||||
mdelay(10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_set_link_rate(struct apple_epic_service *service,
|
||||
const void *data, size_t data_size,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
const struct dptxport_apcall_link_rate *request = data;
|
||||
struct dptxport_apcall_link_rate *reply = reply_;
|
||||
u32 link_rate, phy_link_rate;
|
||||
bool phy_set_rate = false;
|
||||
int ret;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -EINVAL;
|
||||
if (data_size < sizeof(*request))
|
||||
return -EINVAL;
|
||||
|
||||
link_rate = le32_to_cpu(request->link_rate);
|
||||
trace_dptxport_call_set_link_rate(dptx, link_rate);
|
||||
|
||||
switch (link_rate) {
|
||||
case LINK_RATE_RBR:
|
||||
phy_link_rate = 1620;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case LINK_RATE_HBR:
|
||||
phy_link_rate = 2700;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case LINK_RATE_HBR2:
|
||||
phy_link_rate = 5400;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case LINK_RATE_HBR3:
|
||||
phy_link_rate = 8100;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
case 0:
|
||||
phy_link_rate = 0;
|
||||
phy_set_rate = true;
|
||||
break;
|
||||
default:
|
||||
dev_err(service->ep->dcp->dev,
|
||||
"DPTXPort: Unsupported link rate 0x%x requested\n",
|
||||
link_rate);
|
||||
link_rate = 0;
|
||||
phy_set_rate = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (phy_set_rate) {
|
||||
dptx->phy_ops.dp.link_rate = phy_link_rate;
|
||||
dptx->phy_ops.dp.set_rate = 1;
|
||||
|
||||
if (dptx->atcphy) {
|
||||
ret = phy_configure(dptx->atcphy, &dptx->phy_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
//if (dptx->phy_ops.dp.set_rate)
|
||||
dptx->link_rate = dptx->pending_link_rate = link_rate;
|
||||
|
||||
}
|
||||
|
||||
//dptx->pending_link_rate = link_rate;
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
reply->link_rate = cpu_to_le32(link_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call_get_supports_hpd(struct apple_epic_service *service,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_get_support *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -EINVAL;
|
||||
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
reply->supported = cpu_to_le32(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_get_supports_downspread(struct apple_epic_service *service,
|
||||
void *reply_, size_t reply_size)
|
||||
{
|
||||
struct dptxport_apcall_get_support *reply = reply_;
|
||||
|
||||
if (reply_size < sizeof(*reply))
|
||||
return -EINVAL;
|
||||
|
||||
reply->retcode = cpu_to_le32(0);
|
||||
reply->supported = cpu_to_le32(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_activate(struct apple_epic_service *service,
|
||||
const void *data, size_t data_size,
|
||||
void *reply, size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
const struct apple_dcp *dcp = service->ep->dcp;
|
||||
|
||||
// TODO: hack, use phy_set_mode to select the correct DCP(EXT) input
|
||||
phy_set_mode_ext(dptx->atcphy, PHY_MODE_DP, dcp->index);
|
||||
|
||||
memcpy(reply, data, min(reply_size, data_size));
|
||||
if (reply_size >= 4)
|
||||
memset(reply, 0, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dptxport_call_deactivate(struct apple_epic_service *service,
|
||||
const void *data, size_t data_size,
|
||||
void *reply, size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
|
||||
/* deactivate phy */
|
||||
phy_set_mode_ext(dptx->atcphy, PHY_MODE_INVALID, 0);
|
||||
|
||||
memcpy(reply, data, min(reply_size, data_size));
|
||||
if (reply_size >= 4)
|
||||
memset(reply, 0, 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dptxport_call(struct apple_epic_service *service, u32 idx,
|
||||
const void *data, size_t data_size, void *reply,
|
||||
size_t reply_size)
|
||||
{
|
||||
struct dptx_port *dptx = service->cookie;
|
||||
trace_dptxport_apcall(dptx, idx, data_size);
|
||||
|
||||
switch (idx) {
|
||||
case DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG:
|
||||
return dptxport_call_will_change_link_config(service);
|
||||
case DPTX_APCALL_DID_CHANGE_LINK_CONFIG:
|
||||
return dptxport_call_did_change_link_config(service);
|
||||
case DPTX_APCALL_GET_MAX_LINK_RATE:
|
||||
return dptxport_call_get_max_link_rate(service, reply,
|
||||
reply_size);
|
||||
case DPTX_APCALL_GET_LINK_RATE:
|
||||
return dptxport_call_get_link_rate(service, reply, reply_size);
|
||||
case DPTX_APCALL_SET_LINK_RATE:
|
||||
return dptxport_call_set_link_rate(service, data, data_size,
|
||||
reply, reply_size);
|
||||
case DPTX_APCALL_GET_MAX_LANE_COUNT:
|
||||
return dptxport_call_get_max_lane_count(service, reply, reply_size);
|
||||
case DPTX_APCALL_SET_ACTIVE_LANE_COUNT:
|
||||
return dptxport_call_set_active_lane_count(service, data, data_size,
|
||||
reply, reply_size);
|
||||
case DPTX_APCALL_GET_SUPPORTS_HPD:
|
||||
return dptxport_call_get_supports_hpd(service, reply,
|
||||
reply_size);
|
||||
case DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD:
|
||||
return dptxport_call_get_supports_downspread(service, reply,
|
||||
reply_size);
|
||||
case DPTX_APCALL_GET_MAX_DRIVE_SETTINGS:
|
||||
return dptxport_call_get_max_drive_settings(service, reply,
|
||||
reply_size);
|
||||
case DPTX_APCALL_GET_DRIVE_SETTINGS:
|
||||
return dptxport_call_get_drive_settings(service, data, data_size,
|
||||
reply, reply_size);
|
||||
case DPTX_APCALL_SET_DRIVE_SETTINGS:
|
||||
return dptxport_call_set_drive_settings(service, data, data_size,
|
||||
reply, reply_size);
|
||||
case DPTX_APCALL_ACTIVATE:
|
||||
return dptxport_call_activate(service, data, data_size,
|
||||
reply, reply_size);
|
||||
case DPTX_APCALL_DEACTIVATE:
|
||||
return dptxport_call_deactivate(service, data, data_size,
|
||||
reply, reply_size);
|
||||
default:
|
||||
/* just try to ACK and hope for the best... */
|
||||
dev_info(service->ep->dcp->dev, "DPTXPort: acking unhandled call %u\n",
|
||||
idx);
|
||||
memcpy(reply, data, min(reply_size, data_size));
|
||||
if (reply_size >= 4)
|
||||
memset(reply, 0, 4);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void dptxport_init(struct apple_epic_service *service, const char *name,
|
||||
const char *class, s64 unit)
|
||||
{
|
||||
|
||||
if (strcmp(name, "dcpdptx-port-epic"))
|
||||
return;
|
||||
if (strcmp(class, "AppleDCPDPTXRemotePort"))
|
||||
return;
|
||||
|
||||
trace_dptxport_init(service->ep->dcp, unit);
|
||||
|
||||
switch (unit) {
|
||||
case 0:
|
||||
case 1:
|
||||
if (service->ep->dcp->dptxport[unit].enabled) {
|
||||
dev_err(service->ep->dcp->dev,
|
||||
"DPTXPort: unit %lld already exists\n", unit);
|
||||
return;
|
||||
}
|
||||
service->ep->dcp->dptxport[unit].unit = unit;
|
||||
service->ep->dcp->dptxport[unit].service = service;
|
||||
service->ep->dcp->dptxport[unit].enabled = true;
|
||||
service->cookie = (void *)&service->ep->dcp->dptxport[unit];
|
||||
complete(&service->ep->dcp->dptxport[unit].enable_completion);
|
||||
break;
|
||||
default:
|
||||
dev_err(service->ep->dcp->dev, "DPTXPort: invalid unit %lld\n",
|
||||
unit);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct apple_epic_service_ops dptxep_ops[] = {
|
||||
{
|
||||
.name = "AppleDCPDPTXRemotePort",
|
||||
.init = dptxport_init,
|
||||
.call = dptxport_call,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
int dptxep_init(struct apple_dcp *dcp)
|
||||
{
|
||||
int ret;
|
||||
u32 port;
|
||||
unsigned long timeout = msecs_to_jiffies(1000);
|
||||
|
||||
init_completion(&dcp->dptxport[0].enable_completion);
|
||||
init_completion(&dcp->dptxport[1].enable_completion);
|
||||
init_completion(&dcp->dptxport[0].linkcfg_completion);
|
||||
init_completion(&dcp->dptxport[1].linkcfg_completion);
|
||||
|
||||
dcp->dptxep = afk_init(dcp, DPTX_ENDPOINT, dptxep_ops);
|
||||
if (IS_ERR(dcp->dptxep))
|
||||
return PTR_ERR(dcp->dptxep);
|
||||
|
||||
ret = afk_start(dcp->dptxep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (port = 0; port < dcp->hw.num_dptx_ports; port++) {
|
||||
ret = wait_for_completion_timeout(&dcp->dptxport[port].enable_completion,
|
||||
timeout);
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
timeout = ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
70
sys/dev/pci/drm/apple/dptxep.h
Normal file
70
sys/dev/pci/drm/apple/dptxep.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
#ifndef __APPLE_DCP_DPTXEP_H__
|
||||
#define __APPLE_DCP_DPTXEP_H__
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/mux/consumer.h>
|
||||
|
||||
enum dptx_apcall {
|
||||
DPTX_APCALL_ACTIVATE = 0,
|
||||
DPTX_APCALL_DEACTIVATE = 1,
|
||||
DPTX_APCALL_GET_MAX_DRIVE_SETTINGS = 2,
|
||||
DPTX_APCALL_SET_DRIVE_SETTINGS = 3,
|
||||
DPTX_APCALL_GET_DRIVE_SETTINGS = 4,
|
||||
DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG = 5,
|
||||
DPTX_APCALL_DID_CHANGE_LINK_CONFIG = 6,
|
||||
DPTX_APCALL_GET_MAX_LINK_RATE = 7,
|
||||
DPTX_APCALL_GET_LINK_RATE = 8,
|
||||
DPTX_APCALL_SET_LINK_RATE = 9,
|
||||
DPTX_APCALL_GET_MAX_LANE_COUNT = 10,
|
||||
DPTX_APCALL_GET_ACTIVE_LANE_COUNT = 11,
|
||||
DPTX_APCALL_SET_ACTIVE_LANE_COUNT = 12,
|
||||
DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD = 13,
|
||||
DPTX_APCALL_GET_DOWN_SPREAD = 14,
|
||||
DPTX_APCALL_SET_DOWN_SPREAD = 15,
|
||||
DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING = 16,
|
||||
DPTX_APCALL_SET_LANE_MAP = 17,
|
||||
DPTX_APCALL_GET_SUPPORTS_HPD = 18,
|
||||
DPTX_APCALL_FORCE_HOTPLUG_DETECT = 19,
|
||||
DPTX_APCALL_INACTIVE_SINK_DETECTED = 20,
|
||||
DPTX_APCALL_SET_TILED_DISPLAY_HINTS = 21,
|
||||
DPTX_APCALL_DEVICE_NOT_RESPONDING = 22,
|
||||
DPTX_APCALL_DEVICE_BUSY_TIMEOUT = 23,
|
||||
DPTX_APCALL_DEVICE_NOT_STARTED = 24,
|
||||
};
|
||||
|
||||
#define DCPDPTX_REMOTE_PORT_CORE GENMASK(3, 0)
|
||||
#define DCPDPTX_REMOTE_PORT_ATC GENMASK(7, 4)
|
||||
#define DCPDPTX_REMOTE_PORT_DIE GENMASK(11, 8)
|
||||
#define DCPDPTX_REMOTE_PORT_CONNECTED BIT(15)
|
||||
|
||||
enum dptx_link_rate {
|
||||
LINK_RATE_RBR = 0x06,
|
||||
LINK_RATE_HBR = 0x0a,
|
||||
LINK_RATE_HBR2 = 0x14,
|
||||
LINK_RATE_HBR3 = 0x1e,
|
||||
};
|
||||
|
||||
struct apple_epic_service;
|
||||
|
||||
struct dptx_port {
|
||||
bool enabled, connected;
|
||||
struct completion enable_completion;
|
||||
struct completion linkcfg_completion;
|
||||
u32 unit;
|
||||
struct apple_epic_service *service;
|
||||
union phy_configure_opts phy_ops;
|
||||
struct phy *atcphy;
|
||||
struct mux_control *mux;
|
||||
u32 lane_count;
|
||||
u32 link_rate, pending_link_rate;
|
||||
u32 drive_settings[2];
|
||||
};
|
||||
|
||||
int dptxport_validate_connection(struct apple_epic_service *service, u8 core,
|
||||
u8 atc, u8 die);
|
||||
int dptxport_connect(struct apple_epic_service *service, u8 core, u8 atc,
|
||||
u8 die);
|
||||
int dptxport_request_display(struct apple_epic_service *service);
|
||||
int dptxport_release_display(struct apple_epic_service *service);
|
||||
int dptxport_set_hpd(struct apple_epic_service *service, bool hpd);
|
||||
#endif
|
29
sys/dev/pci/drm/apple/ibootep.c
Normal file
29
sys/dev/pci/drm/apple/ibootep.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2023 */
|
||||
|
||||
#include <linux/completion.h>
|
||||
|
||||
#include "afk.h"
|
||||
#include "dcp.h"
|
||||
|
||||
static void disp_service_init(struct apple_epic_service *service, const char *name,
|
||||
const char *class, s64 unit)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static const struct apple_epic_service_ops ibootep_ops[] = {
|
||||
{
|
||||
.name = "disp0-service",
|
||||
.init = disp_service_init,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
int ibootep_init(struct apple_dcp *dcp)
|
||||
{
|
||||
dcp->ibootep = afk_init(dcp, DISP0_ENDPOINT, ibootep_ops);
|
||||
afk_start(dcp->ibootep);
|
||||
|
||||
return 0;
|
||||
}
|
571
sys/dev/pci/drm/apple/iomfb.c
Normal file
571
sys/dev/pci/drm/apple/iomfb.c
Normal file
|
@ -0,0 +1,571 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
#include <linux/align.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/soc/apple/rtkit.h>
|
||||
|
||||
#include <drm/drm_fb_dma_helper.h>
|
||||
#include <drm/drm_fourcc.h>
|
||||
#include <drm/drm_framebuffer.h>
|
||||
#include <drm/drm_gem_dma_helper.h>
|
||||
#include <drm/drm_probe_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
|
||||
#include "dcp.h"
|
||||
#include "dcp-internal.h"
|
||||
#include "iomfb.h"
|
||||
#include "iomfb_internal.h"
|
||||
#include "parser.h"
|
||||
#include "trace.h"
|
||||
|
||||
static int dcp_tx_offset(enum dcp_context_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case DCP_CONTEXT_CB:
|
||||
case DCP_CONTEXT_CMD:
|
||||
return 0x00000;
|
||||
case DCP_CONTEXT_OOBCB:
|
||||
case DCP_CONTEXT_OOBCMD:
|
||||
return 0x08000;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dcp_channel_offset(enum dcp_context_id id)
|
||||
{
|
||||
switch (id) {
|
||||
case DCP_CONTEXT_ASYNC:
|
||||
return 0x40000;
|
||||
case DCP_CONTEXT_OOBASYNC:
|
||||
return 0x48000;
|
||||
case DCP_CONTEXT_CB:
|
||||
return 0x60000;
|
||||
case DCP_CONTEXT_OOBCB:
|
||||
return 0x68000;
|
||||
default:
|
||||
return dcp_tx_offset(id);
|
||||
}
|
||||
}
|
||||
|
||||
static inline u64 dcpep_set_shmem(u64 dart_va)
|
||||
{
|
||||
return FIELD_PREP(IOMFB_MESSAGE_TYPE, IOMFB_MESSAGE_TYPE_SET_SHMEM) |
|
||||
FIELD_PREP(IOMFB_SHMEM_FLAG, IOMFB_SHMEM_FLAG_VALUE) |
|
||||
FIELD_PREP(IOMFB_SHMEM_DVA, dart_va);
|
||||
}
|
||||
|
||||
static inline u64 dcpep_msg(enum dcp_context_id id, u32 length, u16 offset)
|
||||
{
|
||||
return FIELD_PREP(IOMFB_MESSAGE_TYPE, IOMFB_MESSAGE_TYPE_MSG) |
|
||||
FIELD_PREP(IOMFB_MSG_CONTEXT, id) |
|
||||
FIELD_PREP(IOMFB_MSG_OFFSET, offset) |
|
||||
FIELD_PREP(IOMFB_MSG_LENGTH, length);
|
||||
}
|
||||
|
||||
static inline u64 dcpep_ack(enum dcp_context_id id)
|
||||
{
|
||||
return dcpep_msg(id, 0, 0) | IOMFB_MSG_ACK;
|
||||
}
|
||||
|
||||
/*
|
||||
* A channel is busy if we have sent a message that has yet to be
|
||||
* acked. The driver must not sent a message to a busy channel.
|
||||
*/
|
||||
static bool dcp_channel_busy(struct dcp_channel *ch)
|
||||
{
|
||||
return (ch->depth != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the context ID passed to the DCP for a command we push. The rule is
|
||||
* simple: callback contexts are used when replying to the DCP, command
|
||||
* contexts are used otherwise. That corresponds to a non/zero call stack
|
||||
* depth. This rule frees the caller from tracking the call context manually.
|
||||
*/
|
||||
static enum dcp_context_id dcp_call_context(struct apple_dcp *dcp, bool oob)
|
||||
{
|
||||
u8 depth = oob ? dcp->ch_oobcmd.depth : dcp->ch_cmd.depth;
|
||||
|
||||
if (depth)
|
||||
return oob ? DCP_CONTEXT_OOBCB : DCP_CONTEXT_CB;
|
||||
else
|
||||
return oob ? DCP_CONTEXT_OOBCMD : DCP_CONTEXT_CMD;
|
||||
}
|
||||
|
||||
/* Get a channel for a context */
|
||||
static struct dcp_channel *dcp_get_channel(struct apple_dcp *dcp,
|
||||
enum dcp_context_id context)
|
||||
{
|
||||
switch (context) {
|
||||
case DCP_CONTEXT_CB:
|
||||
return &dcp->ch_cb;
|
||||
case DCP_CONTEXT_CMD:
|
||||
return &dcp->ch_cmd;
|
||||
case DCP_CONTEXT_OOBCB:
|
||||
return &dcp->ch_oobcb;
|
||||
case DCP_CONTEXT_OOBCMD:
|
||||
return &dcp->ch_oobcmd;
|
||||
case DCP_CONTEXT_ASYNC:
|
||||
return &dcp->ch_async;
|
||||
case DCP_CONTEXT_OOBASYNC:
|
||||
return &dcp->ch_oobasync;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the start of a packet: after the end of the previous packet */
|
||||
static u16 dcp_packet_start(struct dcp_channel *ch, u8 depth)
|
||||
{
|
||||
if (depth > 0)
|
||||
return ch->end[depth - 1];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pushes and pops the depth of the call stack with safety checks */
|
||||
static u8 dcp_push_depth(u8 *depth)
|
||||
{
|
||||
u8 ret = (*depth)++;
|
||||
|
||||
WARN_ON(ret >= DCP_MAX_CALL_DEPTH);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 dcp_pop_depth(u8 *depth)
|
||||
{
|
||||
WARN_ON((*depth) == 0);
|
||||
|
||||
return --(*depth);
|
||||
}
|
||||
|
||||
/* Call a DCP function given by a tag */
|
||||
void dcp_push(struct apple_dcp *dcp, bool oob, const struct dcp_method_entry *call,
|
||||
u32 in_len, u32 out_len, void *data, dcp_callback_t cb,
|
||||
void *cookie)
|
||||
{
|
||||
enum dcp_context_id context = dcp_call_context(dcp, oob);
|
||||
struct dcp_channel *ch = dcp_get_channel(dcp, context);
|
||||
|
||||
struct dcp_packet_header header = {
|
||||
.in_len = in_len,
|
||||
.out_len = out_len,
|
||||
|
||||
/* Tag is reversed due to endianness of the fourcc */
|
||||
.tag[0] = call->tag[3],
|
||||
.tag[1] = call->tag[2],
|
||||
.tag[2] = call->tag[1],
|
||||
.tag[3] = call->tag[0],
|
||||
};
|
||||
|
||||
u8 depth = dcp_push_depth(&ch->depth);
|
||||
u16 offset = dcp_packet_start(ch, depth);
|
||||
|
||||
void *out = dcp->shmem + dcp_tx_offset(context) + offset;
|
||||
void *out_data = out + sizeof(header);
|
||||
size_t data_len = sizeof(header) + in_len + out_len;
|
||||
|
||||
memcpy(out, &header, sizeof(header));
|
||||
|
||||
if (in_len > 0)
|
||||
memcpy(out_data, data, in_len);
|
||||
|
||||
trace_iomfb_push(dcp, call, context, offset, depth);
|
||||
|
||||
ch->callbacks[depth] = cb;
|
||||
ch->cookies[depth] = cookie;
|
||||
ch->output[depth] = out + sizeof(header) + in_len;
|
||||
ch->end[depth] = offset + ALIGN(data_len, DCP_PACKET_ALIGNMENT);
|
||||
|
||||
dcp_send_message(dcp, IOMFB_ENDPOINT,
|
||||
dcpep_msg(context, data_len, offset));
|
||||
}
|
||||
|
||||
/* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */
|
||||
int dcp_parse_tag(char tag[4])
|
||||
{
|
||||
u32 d[3];
|
||||
int i;
|
||||
|
||||
if (tag[3] != 'D')
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
d[i] = (u32)(tag[i] - '0');
|
||||
|
||||
if (d[i] > 9)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return d[0] + (d[1] * 10) + (d[2] * 100);
|
||||
}
|
||||
|
||||
/* Ack a callback from the DCP */
|
||||
void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context)
|
||||
{
|
||||
struct dcp_channel *ch = dcp_get_channel(dcp, context);
|
||||
|
||||
dcp_pop_depth(&ch->depth);
|
||||
dcp_send_message(dcp, IOMFB_ENDPOINT,
|
||||
dcpep_ack(context));
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper to send a DRM hotplug event. The DCP is accessed from a single
|
||||
* (RTKit) thread. To handle hotplug callbacks, we need to call
|
||||
* drm_kms_helper_hotplug_event, which does an atomic commit (via DCP) and
|
||||
* waits for vblank (a DCP callback). That means we deadlock if we call from
|
||||
* the RTKit thread! Instead, move the call to another thread via a workqueue.
|
||||
*/
|
||||
void dcp_hotplug(struct work_struct *work)
|
||||
{
|
||||
struct apple_connector *connector;
|
||||
struct apple_dcp *dcp;
|
||||
|
||||
connector = container_of(work, struct apple_connector, hotplug_wq);
|
||||
|
||||
dcp = platform_get_drvdata(connector->dcp);
|
||||
dev_info(dcp->dev, "%s() connected:%d valid_mode:%d nr_modes:%u\n", __func__,
|
||||
connector->connected, dcp->valid_mode, dcp->nr_modes);
|
||||
|
||||
/*
|
||||
* DCP defers link training until we set a display mode. But we set
|
||||
* display modes from atomic_flush, so userspace needs to trigger a
|
||||
* flush, or the CRTC gets no signal.
|
||||
*/
|
||||
if (connector->base.state && !dcp->valid_mode && connector->connected)
|
||||
drm_connector_set_link_status_property(&connector->base,
|
||||
DRM_MODE_LINK_STATUS_BAD);
|
||||
|
||||
drm_kms_helper_connector_hotplug_event(&connector->base);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dcp_hotplug);
|
||||
|
||||
static void dcpep_handle_cb(struct apple_dcp *dcp, enum dcp_context_id context,
|
||||
void *data, u32 length, u16 offset)
|
||||
{
|
||||
struct device *dev = dcp->dev;
|
||||
struct dcp_packet_header *hdr = data;
|
||||
void *in, *out;
|
||||
int tag = dcp_parse_tag(hdr->tag);
|
||||
struct dcp_channel *ch = dcp_get_channel(dcp, context);
|
||||
u8 depth;
|
||||
|
||||
if (tag < 0 || tag >= IOMFB_MAX_CB || !dcp->cb_handlers || !dcp->cb_handlers[tag]) {
|
||||
dev_warn(dev, "received unknown callback %c%c%c%c\n",
|
||||
hdr->tag[3], hdr->tag[2], hdr->tag[1], hdr->tag[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
in = data + sizeof(*hdr);
|
||||
out = in + hdr->in_len;
|
||||
|
||||
// TODO: verify that in_len and out_len match our prototypes
|
||||
// for now just clear the out data to have at least consistant results
|
||||
if (hdr->out_len)
|
||||
memset(out, 0, hdr->out_len);
|
||||
|
||||
depth = dcp_push_depth(&ch->depth);
|
||||
ch->output[depth] = out;
|
||||
ch->end[depth] = offset + ALIGN(length, DCP_PACKET_ALIGNMENT);
|
||||
|
||||
if (dcp->cb_handlers[tag](dcp, tag, out, in))
|
||||
dcp_ack(dcp, context);
|
||||
}
|
||||
|
||||
static void dcpep_handle_ack(struct apple_dcp *dcp, enum dcp_context_id context,
|
||||
void *data, u32 length)
|
||||
{
|
||||
struct dcp_packet_header *header = data;
|
||||
struct dcp_channel *ch = dcp_get_channel(dcp, context);
|
||||
void *cookie;
|
||||
dcp_callback_t cb;
|
||||
|
||||
if (!ch) {
|
||||
dev_warn(dcp->dev, "ignoring ack on context %X\n", context);
|
||||
return;
|
||||
}
|
||||
|
||||
dcp_pop_depth(&ch->depth);
|
||||
|
||||
cb = ch->callbacks[ch->depth];
|
||||
cookie = ch->cookies[ch->depth];
|
||||
|
||||
ch->callbacks[ch->depth] = NULL;
|
||||
ch->cookies[ch->depth] = NULL;
|
||||
|
||||
if (cb)
|
||||
cb(dcp, data + sizeof(*header) + header->in_len, cookie);
|
||||
}
|
||||
|
||||
static void dcpep_got_msg(struct apple_dcp *dcp, u64 message)
|
||||
{
|
||||
enum dcp_context_id ctx_id;
|
||||
u16 offset;
|
||||
u32 length;
|
||||
int channel_offset;
|
||||
void *data;
|
||||
|
||||
ctx_id = FIELD_GET(IOMFB_MSG_CONTEXT, message);
|
||||
offset = FIELD_GET(IOMFB_MSG_OFFSET, message);
|
||||
length = FIELD_GET(IOMFB_MSG_LENGTH, message);
|
||||
|
||||
channel_offset = dcp_channel_offset(ctx_id);
|
||||
|
||||
if (channel_offset < 0) {
|
||||
dev_warn(dcp->dev, "invalid context received %u", ctx_id);
|
||||
return;
|
||||
}
|
||||
|
||||
data = dcp->shmem + channel_offset + offset;
|
||||
|
||||
if (FIELD_GET(IOMFB_MSG_ACK, message))
|
||||
dcpep_handle_ack(dcp, ctx_id, data, length);
|
||||
else
|
||||
dcpep_handle_cb(dcp, ctx_id, data, length, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* DRM specifies rectangles as start and end coordinates. DCP specifies
|
||||
* rectangles as a start coordinate and a width/height. Convert a DRM rectangle
|
||||
* to a DCP rectangle.
|
||||
*/
|
||||
struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect)
|
||||
{
|
||||
return (struct dcp_rect){ .x = rect->x1,
|
||||
.y = rect->y1,
|
||||
.w = drm_rect_width(rect),
|
||||
.h = drm_rect_height(rect) };
|
||||
}
|
||||
|
||||
u32 drm_format_to_dcp(u32 drm)
|
||||
{
|
||||
switch (drm) {
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
return fourcc_code('A', 'R', 'G', 'B');
|
||||
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
return fourcc_code('A', 'B', 'G', 'R');
|
||||
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
return fourcc_code('r', '0', '3', 'w');
|
||||
}
|
||||
|
||||
pr_warn("DRM format %X not supported in DCP\n", drm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dcp_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct apple_connector *apple_connector = to_apple_connector(connector);
|
||||
struct platform_device *pdev = apple_connector->dcp;
|
||||
struct apple_dcp *dcp = platform_get_drvdata(pdev);
|
||||
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_display_mode *mode;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dcp->nr_modes; ++i) {
|
||||
mode = drm_mode_duplicate(dev, &dcp->modes[i].mode);
|
||||
|
||||
if (!mode) {
|
||||
dev_err(dev->dev, "Failed to duplicate display mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
drm_mode_probed_add(connector, mode);
|
||||
}
|
||||
|
||||
return dcp->nr_modes;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dcp_get_modes);
|
||||
|
||||
/* The user may own drm_display_mode, so we need to search for our copy */
|
||||
struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dcp->nr_modes; ++i) {
|
||||
if (drm_mode_match(mode, &dcp->modes[i].mode,
|
||||
DRM_MODE_MATCH_TIMINGS |
|
||||
DRM_MODE_MATCH_CLOCK))
|
||||
return &dcp->modes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int dcp_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct apple_connector *apple_connector = to_apple_connector(connector);
|
||||
struct platform_device *pdev = apple_connector->dcp;
|
||||
struct apple_dcp *dcp = platform_get_drvdata(pdev);
|
||||
|
||||
return lookup_mode(dcp, mode) ? MODE_OK : MODE_BAD;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dcp_mode_valid);
|
||||
|
||||
int dcp_crtc_atomic_modeset(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct apple_crtc *apple_crtc = to_apple_crtc(crtc);
|
||||
struct apple_dcp *dcp = platform_get_drvdata(apple_crtc->dcp);
|
||||
struct drm_crtc_state *crtc_state;
|
||||
int ret = -EIO;
|
||||
bool modeset;
|
||||
|
||||
crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
|
||||
if (!crtc_state)
|
||||
return 0;
|
||||
|
||||
modeset = drm_atomic_crtc_needs_modeset(crtc_state) || !dcp->valid_mode;
|
||||
|
||||
if (!modeset)
|
||||
return 0;
|
||||
|
||||
/* ignore no mode, poweroff is handled elsewhere */
|
||||
if (crtc_state->mode.hdisplay == 0 && crtc_state->mode.vdisplay == 0)
|
||||
return 0;
|
||||
|
||||
switch (dcp->fw_compat) {
|
||||
case DCP_FIRMWARE_V_12_3:
|
||||
ret = iomfb_modeset_v12_3(dcp, crtc_state);
|
||||
break;
|
||||
case DCP_FIRMWARE_V_13_5:
|
||||
ret = iomfb_modeset_v13_3(dcp, crtc_state);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(true, "Unexpected firmware version: %u\n",
|
||||
dcp->fw_compat);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dcp_crtc_atomic_modeset);
|
||||
|
||||
bool dcp_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct apple_crtc *apple_crtc = to_apple_crtc(crtc);
|
||||
struct platform_device *pdev = apple_crtc->dcp;
|
||||
struct apple_dcp *dcp = platform_get_drvdata(pdev);
|
||||
|
||||
/* TODO: support synthesized modes through scaling */
|
||||
return lookup_mode(dcp, mode) != NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(dcp_crtc_mode_fixup);
|
||||
|
||||
|
||||
void dcp_flush(struct drm_crtc *crtc, struct drm_atomic_state *state)
|
||||
{
|
||||
struct platform_device *pdev = to_apple_crtc(crtc)->dcp;
|
||||
struct apple_dcp *dcp = platform_get_drvdata(pdev);
|
||||
|
||||
if (dcp_channel_busy(&dcp->ch_cmd))
|
||||
{
|
||||
dev_err(dcp->dev, "unexpected busy command channel");
|
||||
/* HACK: issue a delayed vblank event to avoid timeouts in
|
||||
* drm_atomic_helper_wait_for_vblanks().
|
||||
*/
|
||||
schedule_work(&dcp->vblank_wq);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (dcp->fw_compat) {
|
||||
case DCP_FIRMWARE_V_12_3:
|
||||
iomfb_flush_v12_3(dcp, crtc, state);
|
||||
break;
|
||||
case DCP_FIRMWARE_V_13_5:
|
||||
iomfb_flush_v13_3(dcp, crtc, state);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dcp_flush);
|
||||
|
||||
static void iomfb_start(struct apple_dcp *dcp)
|
||||
{
|
||||
switch (dcp->fw_compat) {
|
||||
case DCP_FIRMWARE_V_12_3:
|
||||
iomfb_start_v12_3(dcp);
|
||||
break;
|
||||
case DCP_FIRMWARE_V_13_5:
|
||||
iomfb_start_v13_3(dcp);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool dcp_is_initialized(struct platform_device *pdev)
|
||||
{
|
||||
struct apple_dcp *dcp = platform_get_drvdata(pdev);
|
||||
|
||||
return dcp->active;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dcp_is_initialized);
|
||||
|
||||
void iomfb_recv_msg(struct apple_dcp *dcp, u64 message)
|
||||
{
|
||||
enum dcpep_type type = FIELD_GET(IOMFB_MESSAGE_TYPE, message);
|
||||
|
||||
if (type == IOMFB_MESSAGE_TYPE_INITIALIZED)
|
||||
iomfb_start(dcp);
|
||||
else if (type == IOMFB_MESSAGE_TYPE_MSG)
|
||||
dcpep_got_msg(dcp, message);
|
||||
else
|
||||
dev_warn(dcp->dev, "Ignoring unknown message %llx\n", message);
|
||||
}
|
||||
|
||||
int iomfb_start_rtkit(struct apple_dcp *dcp)
|
||||
{
|
||||
dma_addr_t shmem_iova;
|
||||
apple_rtkit_start_ep(dcp->rtk, IOMFB_ENDPOINT);
|
||||
|
||||
dcp->shmem = dma_alloc_coherent(dcp->dev, DCP_SHMEM_SIZE, &shmem_iova,
|
||||
GFP_KERNEL);
|
||||
|
||||
dcp_send_message(dcp, IOMFB_ENDPOINT, dcpep_set_shmem(shmem_iova));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iomfb_shutdown(struct apple_dcp *dcp)
|
||||
{
|
||||
/* We're going down */
|
||||
dcp->active = false;
|
||||
dcp->valid_mode = false;
|
||||
|
||||
switch (dcp->fw_compat) {
|
||||
case DCP_FIRMWARE_V_12_3:
|
||||
iomfb_shutdown_v12_3(dcp);
|
||||
break;
|
||||
case DCP_FIRMWARE_V_13_5:
|
||||
iomfb_shutdown_v13_3(dcp);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(true, "Unexpected firmware version: %u\n", dcp->fw_compat);
|
||||
break;
|
||||
}
|
||||
}
|
432
sys/dev/pci/drm/apple/iomfb.h
Normal file
432
sys/dev/pci/drm/apple/iomfb.h
Normal file
|
@ -0,0 +1,432 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
#ifndef __APPLE_DCPEP_H__
|
||||
#define __APPLE_DCPEP_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "version_utils.h"
|
||||
|
||||
/* Fixed size of shared memory between DCP and AP */
|
||||
#define DCP_SHMEM_SIZE 0x100000
|
||||
|
||||
/* DCP message contexts */
|
||||
enum dcp_context_id {
|
||||
/* Callback */
|
||||
DCP_CONTEXT_CB = 0,
|
||||
|
||||
/* Command */
|
||||
DCP_CONTEXT_CMD = 2,
|
||||
|
||||
/* Asynchronous */
|
||||
DCP_CONTEXT_ASYNC = 3,
|
||||
|
||||
/* Out-of-band callback */
|
||||
DCP_CONTEXT_OOBCB = 4,
|
||||
|
||||
/* Out-of-band command */
|
||||
DCP_CONTEXT_OOBCMD = 6,
|
||||
|
||||
/* Out-of-band Asynchronous */
|
||||
DCP_CONTEXT_OOBASYNC = 7,
|
||||
|
||||
DCP_NUM_CONTEXTS
|
||||
};
|
||||
|
||||
/* RTKit endpoint message types */
|
||||
enum dcpep_type {
|
||||
/* Set shared memory */
|
||||
IOMFB_MESSAGE_TYPE_SET_SHMEM = 0,
|
||||
|
||||
/* DCP is initialized */
|
||||
IOMFB_MESSAGE_TYPE_INITIALIZED = 1,
|
||||
|
||||
/* Remote procedure call */
|
||||
IOMFB_MESSAGE_TYPE_MSG = 2,
|
||||
};
|
||||
|
||||
#define IOMFB_MESSAGE_TYPE GENMASK_ULL( 3, 0)
|
||||
|
||||
/* Message */
|
||||
#define IOMFB_MSG_LENGTH GENMASK_ULL(63, 32)
|
||||
#define IOMFB_MSG_OFFSET GENMASK_ULL(31, 16)
|
||||
#define IOMFB_MSG_CONTEXT GENMASK_ULL(11, 8)
|
||||
#define IOMFB_MSG_ACK BIT_ULL(6)
|
||||
|
||||
/* Set shmem */
|
||||
#define IOMFB_SHMEM_DVA GENMASK_ULL(63, 16)
|
||||
#define IOMFB_SHMEM_FLAG GENMASK_ULL( 7, 4)
|
||||
#define IOMFB_SHMEM_FLAG_VALUE 4
|
||||
|
||||
struct dcp_packet_header {
|
||||
char tag[4];
|
||||
u32 in_len;
|
||||
u32 out_len;
|
||||
} __packed;
|
||||
|
||||
#define DCP_IS_NULL(ptr) ((ptr) ? 1 : 0)
|
||||
#define DCP_PACKET_ALIGNMENT (0x40)
|
||||
|
||||
enum iomfb_property_id {
|
||||
IOMFB_PROPERTY_NITS = 15, // divide by Brightness_Scale
|
||||
};
|
||||
|
||||
#define IOMFB_BRIGHTNESS_MIN 0x10000000
|
||||
|
||||
/* Structures used in v12.0 firmware */
|
||||
|
||||
#define SWAP_SURFACES 4
|
||||
#define MAX_PLANES 3
|
||||
|
||||
enum dcp_colorspace {
|
||||
DCP_COLORSPACE_BG_SRGB = 0,
|
||||
DCP_COLORSPACE_BG_BT2020 = 9,
|
||||
DCP_COLORSPACE_NATIVE = 12,
|
||||
};
|
||||
|
||||
enum dcp_xfer_func {
|
||||
DCP_XFER_FUNC_SDR = 13,
|
||||
DCP_XFER_FUNC_HDR = 16,
|
||||
};
|
||||
|
||||
struct dcp_iouserclient {
|
||||
/* Handle for the IOUserClient. macOS sets this to a kernel VA. */
|
||||
u64 handle;
|
||||
u32 unk;
|
||||
u8 flag1;
|
||||
u8 flag2;
|
||||
u8 padding[2];
|
||||
} __packed;
|
||||
|
||||
struct dcp_rect {
|
||||
u32 x;
|
||||
u32 y;
|
||||
u32 w;
|
||||
u32 h;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Update background color to struct dcp_swap.bg_color
|
||||
*/
|
||||
#define IOMFB_SET_BACKGROUND BIT(31)
|
||||
|
||||
/* Information describing a plane of a planar compressed surface */
|
||||
struct dcp_plane_info {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 base;
|
||||
u32 offset;
|
||||
u32 stride;
|
||||
u32 size;
|
||||
u16 tile_size;
|
||||
u8 tile_w;
|
||||
u8 tile_h;
|
||||
u32 unk[13];
|
||||
} __packed;
|
||||
|
||||
struct dcp_component_types {
|
||||
u8 count;
|
||||
u8 types[7];
|
||||
} __packed;
|
||||
|
||||
struct dcp_allocate_bandwidth_req {
|
||||
u64 unk1;
|
||||
u64 unk2;
|
||||
u64 unk3;
|
||||
u8 unk1_null;
|
||||
u8 unk2_null;
|
||||
u8 padding[8];
|
||||
} __packed;
|
||||
|
||||
struct dcp_allocate_bandwidth_resp {
|
||||
u64 unk1;
|
||||
u64 unk2;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
struct dcp_rt_bandwidth {
|
||||
u64 unk1;
|
||||
u64 reg_scratch;
|
||||
u64 reg_doorbell;
|
||||
u32 unk2;
|
||||
u32 doorbell_bit;
|
||||
u32 padding[7];
|
||||
} __packed;
|
||||
|
||||
struct frame_sync_props {
|
||||
u8 unk[28];
|
||||
};
|
||||
|
||||
struct dcp_set_frame_sync_props_req {
|
||||
struct frame_sync_props props;
|
||||
u8 frame_sync_props_null;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct dcp_set_frame_sync_props_resp {
|
||||
struct frame_sync_props props;
|
||||
} __packed;
|
||||
|
||||
/* Method calls */
|
||||
|
||||
enum dcpep_method {
|
||||
dcpep_late_init_signal,
|
||||
dcpep_setup_video_limits,
|
||||
dcpep_set_create_dfb,
|
||||
dcpep_start_signal,
|
||||
dcpep_swap_start,
|
||||
dcpep_swap_submit,
|
||||
dcpep_set_display_device,
|
||||
dcpep_set_digital_out_mode,
|
||||
dcpep_create_default_fb,
|
||||
dcpep_set_display_refresh_properties,
|
||||
dcpep_flush_supports_power,
|
||||
dcpep_set_power_state,
|
||||
dcpep_first_client_open,
|
||||
dcpep_set_parameter_dcp,
|
||||
dcpep_enable_disable_video_power_savings,
|
||||
dcpep_is_main_display,
|
||||
iomfbep_a131_pmu_service_matched,
|
||||
iomfbep_a132_backlight_service_matched,
|
||||
iomfbep_a358_vi_set_temperature_hint,
|
||||
iomfbep_get_color_remap_mode,
|
||||
iomfbep_last_client_close,
|
||||
iomfbep_abort_swaps_dcp,
|
||||
iomfbep_set_matrix,
|
||||
dcpep_num_methods
|
||||
};
|
||||
|
||||
#define IOMFB_METHOD(tag, name) [name] = { #name, tag }
|
||||
|
||||
struct dcp_method_entry {
|
||||
const char *name;
|
||||
char tag[4];
|
||||
};
|
||||
|
||||
#define IOMFB_MAX_CB (1000)
|
||||
struct apple_dcp;
|
||||
|
||||
typedef bool (*iomfb_cb_handler)(struct apple_dcp *, int, void *, void *);
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
struct dcp_set_digital_out_mode_req {
|
||||
u32 color_mode_id;
|
||||
u32 timing_mode_id;
|
||||
} __packed;
|
||||
|
||||
struct dcp_map_buf_req {
|
||||
u64 buffer;
|
||||
u8 unk;
|
||||
u8 buf_null;
|
||||
u8 vaddr_null;
|
||||
u8 dva_null;
|
||||
} __packed;
|
||||
|
||||
struct dcp_map_buf_resp {
|
||||
u64 vaddr;
|
||||
u64 dva;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
struct dcp_unmap_buf_resp {
|
||||
u64 buffer;
|
||||
u64 vaddr;
|
||||
u64 dva;
|
||||
u8 unk;
|
||||
u8 buf_null;
|
||||
} __packed;
|
||||
|
||||
struct dcp_allocate_buffer_req {
|
||||
u32 unk0;
|
||||
u64 size;
|
||||
u32 unk2;
|
||||
u8 paddr_null;
|
||||
u8 dva_null;
|
||||
u8 dva_size_null;
|
||||
u8 padding;
|
||||
} __packed;
|
||||
|
||||
struct dcp_allocate_buffer_resp {
|
||||
u64 paddr;
|
||||
u64 dva;
|
||||
u64 dva_size;
|
||||
u32 mem_desc_id;
|
||||
} __packed;
|
||||
|
||||
struct dcp_map_physical_req {
|
||||
u64 paddr;
|
||||
u64 size;
|
||||
u32 flags;
|
||||
u8 dva_null;
|
||||
u8 dva_size_null;
|
||||
u8 padding[2];
|
||||
} __packed;
|
||||
|
||||
struct dcp_map_physical_resp {
|
||||
u64 dva;
|
||||
u64 dva_size;
|
||||
u32 mem_desc_id;
|
||||
} __packed;
|
||||
|
||||
struct dcp_swap_start_req {
|
||||
u32 swap_id;
|
||||
struct dcp_iouserclient client;
|
||||
u8 swap_id_null;
|
||||
u8 client_null;
|
||||
u8 padding[2];
|
||||
} __packed;
|
||||
|
||||
struct dcp_swap_start_resp {
|
||||
u32 swap_id;
|
||||
struct dcp_iouserclient client;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
struct dcp_get_uint_prop_req {
|
||||
char obj[4];
|
||||
char key[0x40];
|
||||
u64 value;
|
||||
u8 value_null;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct dcp_get_uint_prop_resp {
|
||||
u64 value;
|
||||
u8 ret;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct iomfb_sr_set_property_int_req {
|
||||
char obj[4];
|
||||
char key[0x40];
|
||||
u64 value;
|
||||
u8 value_null;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct iomfb_set_fx_prop_req {
|
||||
char obj[4];
|
||||
char key[0x40];
|
||||
u32 value;
|
||||
} __packed;
|
||||
|
||||
struct dcp_set_power_state_req {
|
||||
u64 unklong;
|
||||
u8 unkbool;
|
||||
u8 unkint_null;
|
||||
u8 padding[2];
|
||||
} __packed;
|
||||
|
||||
struct dcp_set_power_state_resp {
|
||||
u32 unkint;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
struct dcp_set_dcpav_prop_chunk_req {
|
||||
char data[0x1000];
|
||||
u32 offset;
|
||||
u32 length;
|
||||
} __packed;
|
||||
|
||||
struct dcp_set_dcpav_prop_end_req {
|
||||
char key[0x40];
|
||||
} __packed;
|
||||
|
||||
struct dcp_set_parameter_dcp {
|
||||
u32 param;
|
||||
u32 value[8];
|
||||
u32 count;
|
||||
} __packed;
|
||||
|
||||
struct dcp_swap_complete_intent_gated {
|
||||
u32 swap_id;
|
||||
u8 unkBool;
|
||||
u32 unkInt;
|
||||
u32 width;
|
||||
u32 height;
|
||||
} __packed;
|
||||
|
||||
struct dcp_read_edt_data_req {
|
||||
char key[0x40];
|
||||
u32 count;
|
||||
u32 value[8];
|
||||
} __packed;
|
||||
|
||||
struct dcp_read_edt_data_resp {
|
||||
u32 value[8];
|
||||
u8 ret;
|
||||
} __packed;
|
||||
|
||||
struct iomfb_property {
|
||||
u32 id;
|
||||
u32 value;
|
||||
} __packed;
|
||||
|
||||
struct iomfb_get_color_remap_mode_req {
|
||||
u32 mode;
|
||||
u8 mode_null;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct iomfb_get_color_remap_mode_resp {
|
||||
u32 mode;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
struct iomfb_last_client_close_req {
|
||||
u8 unkint_null;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct iomfb_last_client_close_resp {
|
||||
u32 unkint;
|
||||
} __packed;
|
||||
|
||||
struct io_user_client {
|
||||
u64 addr;
|
||||
u32 unk;
|
||||
u8 flag1;
|
||||
u8 flag2;
|
||||
u8 pad[2];
|
||||
} __packed;
|
||||
|
||||
struct iomfb_abort_swaps_dcp_req {
|
||||
struct io_user_client client;
|
||||
u8 client_null;
|
||||
u8 pad[3];
|
||||
} __packed;
|
||||
|
||||
struct iomfb_abort_swaps_dcp_resp {
|
||||
struct io_user_client client;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
struct iomfb_set_matrix_req {
|
||||
u32 unk_u32; // maybe length?
|
||||
u64 r[3];
|
||||
u64 g[3];
|
||||
u64 b[3];
|
||||
u8 matrix_null;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct iomfb_set_matrix_resp {
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
struct dcpep_get_tiling_state_req {
|
||||
u32 event;
|
||||
u32 param;
|
||||
u32 value;
|
||||
u8 value_null;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct dcpep_get_tiling_state_resp {
|
||||
u32 value;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
#endif
|
123
sys/dev/pci/drm/apple/iomfb_internal.h
Normal file
123
sys/dev/pci/drm/apple/iomfb_internal.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright The Asahi Linux Contributors */
|
||||
|
||||
#include <drm/drm_modes.h>
|
||||
#include <drm/drm_rect.h>
|
||||
|
||||
#include "dcp-internal.h"
|
||||
|
||||
struct apple_dcp;
|
||||
|
||||
typedef void (*dcp_callback_t)(struct apple_dcp *, void *, void *);
|
||||
|
||||
|
||||
#define DCP_THUNK_VOID(func, handle) \
|
||||
static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \
|
||||
void *cookie) \
|
||||
{ \
|
||||
dcp_push(dcp, oob, &dcp_methods[handle], 0, 0, NULL, cb, cookie); \
|
||||
}
|
||||
|
||||
#define DCP_THUNK_OUT(func, handle, T) \
|
||||
static void func(struct apple_dcp *dcp, bool oob, dcp_callback_t cb, \
|
||||
void *cookie) \
|
||||
{ \
|
||||
dcp_push(dcp, oob, &dcp_methods[handle], 0, sizeof(T), NULL, cb, cookie); \
|
||||
}
|
||||
|
||||
#define DCP_THUNK_IN(func, handle, T) \
|
||||
static void func(struct apple_dcp *dcp, bool oob, T *data, \
|
||||
dcp_callback_t cb, void *cookie) \
|
||||
{ \
|
||||
dcp_push(dcp, oob, &dcp_methods[handle], sizeof(T), 0, data, cb, cookie); \
|
||||
}
|
||||
|
||||
#define DCP_THUNK_INOUT(func, handle, T_in, T_out) \
|
||||
static void func(struct apple_dcp *dcp, bool oob, T_in *data, \
|
||||
dcp_callback_t cb, void *cookie) \
|
||||
{ \
|
||||
dcp_push(dcp, oob, &dcp_methods[handle], sizeof(T_in), sizeof(T_out), data, \
|
||||
cb, cookie); \
|
||||
}
|
||||
|
||||
#define IOMFB_THUNK_INOUT(name) \
|
||||
static void iomfb_ ## name(struct apple_dcp *dcp, bool oob, \
|
||||
struct iomfb_ ## name ## _req *data, \
|
||||
dcp_callback_t cb, void *cookie) \
|
||||
{ \
|
||||
dcp_push(dcp, oob, &dcp_methods[iomfbep_ ## name], \
|
||||
sizeof(struct iomfb_ ## name ## _req), \
|
||||
sizeof(struct iomfb_ ## name ## _resp), \
|
||||
data, cb, cookie); \
|
||||
}
|
||||
|
||||
/*
|
||||
* Define type-safe trampolines. Define typedefs to enforce type-safety on the
|
||||
* input data (so if the types don't match, gcc errors out).
|
||||
*/
|
||||
|
||||
#define TRAMPOLINE_VOID(func, handler) \
|
||||
static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \
|
||||
{ \
|
||||
trace_iomfb_callback(dcp, tag, #handler); \
|
||||
handler(dcp); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define TRAMPOLINE_IN(func, handler, T_in) \
|
||||
typedef void (*callback_##handler)(struct apple_dcp *, T_in *); \
|
||||
\
|
||||
static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \
|
||||
{ \
|
||||
callback_##handler cb = handler; \
|
||||
\
|
||||
trace_iomfb_callback(dcp, tag, #handler); \
|
||||
cb(dcp, in); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define TRAMPOLINE_INOUT(func, handler, T_in, T_out) \
|
||||
typedef T_out (*callback_##handler)(struct apple_dcp *, T_in *); \
|
||||
\
|
||||
static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \
|
||||
{ \
|
||||
T_out *typed_out = out; \
|
||||
callback_##handler cb = handler; \
|
||||
\
|
||||
trace_iomfb_callback(dcp, tag, #handler); \
|
||||
*typed_out = cb(dcp, in); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
#define TRAMPOLINE_OUT(func, handler, T_out) \
|
||||
static bool __maybe_unused func(struct apple_dcp *dcp, int tag, void *out, void *in) \
|
||||
{ \
|
||||
T_out *typed_out = out; \
|
||||
\
|
||||
trace_iomfb_callback(dcp, tag, #handler); \
|
||||
*typed_out = handler(dcp); \
|
||||
return true; \
|
||||
}
|
||||
|
||||
/* Call a DCP function given by a tag */
|
||||
void dcp_push(struct apple_dcp *dcp, bool oob, const struct dcp_method_entry *call,
|
||||
u32 in_len, u32 out_len, void *data, dcp_callback_t cb,
|
||||
void *cookie);
|
||||
|
||||
/* Parse a callback tag "D123" into the ID 123. Returns -EINVAL on failure. */
|
||||
int dcp_parse_tag(char tag[4]);
|
||||
|
||||
void dcp_ack(struct apple_dcp *dcp, enum dcp_context_id context);
|
||||
|
||||
/*
|
||||
* DRM specifies rectangles as start and end coordinates. DCP specifies
|
||||
* rectangles as a start coordinate and a width/height. Convert a DRM rectangle
|
||||
* to a DCP rectangle.
|
||||
*/
|
||||
struct dcp_rect drm_to_dcp_rect(struct drm_rect *rect);
|
||||
|
||||
u32 drm_format_to_dcp(u32 drm);
|
||||
|
||||
/* The user may own drm_display_mode, so we need to search for our copy */
|
||||
struct dcp_display_mode *lookup_mode(struct apple_dcp *dcp,
|
||||
const struct drm_display_mode *mode);
|
1491
sys/dev/pci/drm/apple/iomfb_template.c
Normal file
1491
sys/dev/pci/drm/apple/iomfb_template.c
Normal file
File diff suppressed because it is too large
Load diff
184
sys/dev/pci/drm/apple/iomfb_template.h
Normal file
184
sys/dev/pci/drm/apple/iomfb_template.h
Normal file
|
@ -0,0 +1,184 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
/*
|
||||
* This file is intended to be included multiple times with IOMFB_VER
|
||||
* defined to declare DCP firmware version dependent structs.
|
||||
*/
|
||||
|
||||
#ifdef DCP_FW_VER
|
||||
|
||||
#include <drm/drm_crtc.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "iomfb.h"
|
||||
#include "version_utils.h"
|
||||
|
||||
struct DCP_FW_NAME(dcp_swap) {
|
||||
u64 ts1;
|
||||
u64 ts2;
|
||||
u64 unk_10[6];
|
||||
u64 flags1;
|
||||
u64 flags2;
|
||||
|
||||
u32 swap_id;
|
||||
|
||||
u32 surf_ids[SWAP_SURFACES];
|
||||
struct dcp_rect src_rect[SWAP_SURFACES];
|
||||
u32 surf_flags[SWAP_SURFACES];
|
||||
u32 surf_unk[SWAP_SURFACES];
|
||||
struct dcp_rect dst_rect[SWAP_SURFACES];
|
||||
u32 swap_enabled;
|
||||
u32 swap_completed;
|
||||
|
||||
u32 bg_color;
|
||||
u8 unk_110[0x1b8];
|
||||
u32 unk_2c8;
|
||||
u8 unk_2cc[0x14];
|
||||
u32 unk_2e0;
|
||||
#if DCP_FW_VER < DCP_FW_VERSION(13, 2, 0)
|
||||
u16 unk_2e2;
|
||||
#else
|
||||
u8 unk_2e2[3];
|
||||
#endif
|
||||
u64 bl_unk;
|
||||
u32 bl_value; // min value is 0x10000000
|
||||
u8 bl_power; // constant 0x40 for on
|
||||
u8 unk_2f3[0x2d];
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u8 unk_320[0x13f];
|
||||
u64 unk_1;
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
/* Information describing a surface */
|
||||
struct DCP_FW_NAME(dcp_surface) {
|
||||
u8 is_tiled;
|
||||
u8 is_tearing_allowed;
|
||||
u8 is_premultiplied;
|
||||
u32 plane_cnt;
|
||||
u32 plane_cnt2;
|
||||
u32 format; /* DCP fourcc */
|
||||
u32 ycbcr_matrix;
|
||||
u8 xfer_func;
|
||||
u8 colorspace;
|
||||
u32 stride;
|
||||
u16 pix_size;
|
||||
u8 pel_w;
|
||||
u8 pel_h;
|
||||
u32 offset;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 buf_size;
|
||||
u64 protection_opts;
|
||||
u32 surface_id;
|
||||
struct dcp_component_types comp_types[MAX_PLANES];
|
||||
u64 has_comp;
|
||||
struct dcp_plane_info planes[MAX_PLANES];
|
||||
u64 has_planes;
|
||||
u32 compression_info[MAX_PLANES][13];
|
||||
u64 has_compr_info;
|
||||
u32 unk_num;
|
||||
u32 unk_denom;
|
||||
#if DCP_FW_VER < DCP_FW_VERSION(13, 2, 0)
|
||||
u8 padding[7];
|
||||
#else
|
||||
u8 padding[47];
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
/* Prototypes */
|
||||
|
||||
struct DCP_FW_NAME(dcp_swap_submit_req) {
|
||||
struct DCP_FW_NAME(dcp_swap) swap;
|
||||
struct DCP_FW_NAME(dcp_surface) surf[SWAP_SURFACES];
|
||||
u64 surf_iova[SWAP_SURFACES];
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u64 unk_u64_a[SWAP_SURFACES];
|
||||
struct DCP_FW_NAME(dcp_surface) surf2[5];
|
||||
u64 surf2_iova[5];
|
||||
#endif
|
||||
u8 unkbool;
|
||||
u64 unkdouble;
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u64 unkU64;
|
||||
u8 unkbool2;
|
||||
#endif
|
||||
u32 clear; // or maybe switch to default fb?
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u32 unkU32Ptr;
|
||||
#endif
|
||||
u8 swap_null;
|
||||
u8 surf_null[SWAP_SURFACES];
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u8 surf2_null[5];
|
||||
#endif
|
||||
u8 unkoutbool_null;
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u8 unkU32Ptr_null;
|
||||
u8 unkU32out_null;
|
||||
#endif
|
||||
u8 padding[1];
|
||||
} __packed;
|
||||
|
||||
struct DCP_FW_NAME(dcp_swap_submit_resp) {
|
||||
u8 unkoutbool;
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u32 unkU32out;
|
||||
#endif
|
||||
u32 ret;
|
||||
u8 padding[3];
|
||||
} __packed;
|
||||
|
||||
struct DCP_FW_NAME(dc_swap_complete_resp) {
|
||||
u32 swap_id;
|
||||
u8 unkbool;
|
||||
u64 swap_data;
|
||||
#if DCP_FW_VER < DCP_FW_VERSION(13, 2, 0)
|
||||
u8 swap_info[0x6c4];
|
||||
#else
|
||||
u8 swap_info[0x6c5];
|
||||
#endif
|
||||
u32 unkint;
|
||||
u8 swap_info_null;
|
||||
} __packed;
|
||||
|
||||
struct DCP_FW_NAME(dcp_map_reg_req) {
|
||||
char obj[4];
|
||||
u32 index;
|
||||
u32 flags;
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u8 unk_u64_null;
|
||||
#endif
|
||||
u8 addr_null;
|
||||
u8 length_null;
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u8 padding[1];
|
||||
#else
|
||||
u8 padding[2];
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
struct DCP_FW_NAME(dcp_map_reg_resp) {
|
||||
#if DCP_FW_VER >= DCP_FW_VERSION(13, 2, 0)
|
||||
u64 dva;
|
||||
#endif
|
||||
u64 addr;
|
||||
u64 length;
|
||||
u32 ret;
|
||||
} __packed;
|
||||
|
||||
|
||||
struct apple_dcp;
|
||||
|
||||
int DCP_FW_NAME(iomfb_modeset)(struct apple_dcp *dcp,
|
||||
struct drm_crtc_state *crtc_state);
|
||||
void DCP_FW_NAME(iomfb_flush)(struct apple_dcp *dcp, struct drm_crtc *crtc, struct drm_atomic_state *state);
|
||||
void DCP_FW_NAME(iomfb_poweron)(struct apple_dcp *dcp);
|
||||
void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp);
|
||||
void DCP_FW_NAME(iomfb_sleep)(struct apple_dcp *dcp);
|
||||
void DCP_FW_NAME(iomfb_start)(struct apple_dcp *dcp);
|
||||
void DCP_FW_NAME(iomfb_shutdown)(struct apple_dcp *dcp);
|
||||
|
||||
#endif
|
108
sys/dev/pci/drm/apple/iomfb_v12_3.c
Normal file
108
sys/dev/pci/drm/apple/iomfb_v12_3.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright The Asahi Linux Contributors */
|
||||
|
||||
#include "iomfb_v12_3.h"
|
||||
#include "iomfb_v13_3.h"
|
||||
#include "version_utils.h"
|
||||
|
||||
static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = {
|
||||
IOMFB_METHOD("A000", dcpep_late_init_signal),
|
||||
IOMFB_METHOD("A029", dcpep_setup_video_limits),
|
||||
IOMFB_METHOD("A131", iomfbep_a131_pmu_service_matched),
|
||||
IOMFB_METHOD("A132", iomfbep_a132_backlight_service_matched),
|
||||
IOMFB_METHOD("A357", dcpep_set_create_dfb),
|
||||
IOMFB_METHOD("A358", iomfbep_a358_vi_set_temperature_hint),
|
||||
IOMFB_METHOD("A401", dcpep_start_signal),
|
||||
IOMFB_METHOD("A407", dcpep_swap_start),
|
||||
IOMFB_METHOD("A408", dcpep_swap_submit),
|
||||
IOMFB_METHOD("A410", dcpep_set_display_device),
|
||||
IOMFB_METHOD("A411", dcpep_is_main_display),
|
||||
IOMFB_METHOD("A412", dcpep_set_digital_out_mode),
|
||||
IOMFB_METHOD("A422", iomfbep_set_matrix),
|
||||
IOMFB_METHOD("A426", iomfbep_get_color_remap_mode),
|
||||
IOMFB_METHOD("A439", dcpep_set_parameter_dcp),
|
||||
IOMFB_METHOD("A443", dcpep_create_default_fb),
|
||||
IOMFB_METHOD("A447", dcpep_enable_disable_video_power_savings),
|
||||
IOMFB_METHOD("A454", dcpep_first_client_open),
|
||||
IOMFB_METHOD("A455", iomfbep_last_client_close),
|
||||
IOMFB_METHOD("A460", dcpep_set_display_refresh_properties),
|
||||
IOMFB_METHOD("A463", dcpep_flush_supports_power),
|
||||
IOMFB_METHOD("A464", iomfbep_abort_swaps_dcp),
|
||||
IOMFB_METHOD("A468", dcpep_set_power_state),
|
||||
};
|
||||
|
||||
#define DCP_FW v12_3
|
||||
#define DCP_FW_VER DCP_FW_VERSION(12, 3, 0)
|
||||
|
||||
#include "iomfb_template.c"
|
||||
|
||||
static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = {
|
||||
[0] = trampoline_true, /* did_boot_signal */
|
||||
[1] = trampoline_true, /* did_power_on_signal */
|
||||
[2] = trampoline_nop, /* will_power_off_signal */
|
||||
[3] = trampoline_rt_bandwidth,
|
||||
[100] = iomfbep_cb_match_pmu_service,
|
||||
[101] = trampoline_zero, /* get_display_default_stride */
|
||||
[102] = trampoline_nop, /* set_number_property */
|
||||
[103] = trampoline_nop, /* set_boolean_property */
|
||||
[106] = trampoline_nop, /* remove_property */
|
||||
[107] = trampoline_true, /* create_provider_service */
|
||||
[108] = trampoline_true, /* create_product_service */
|
||||
[109] = trampoline_true, /* create_pmu_service */
|
||||
[110] = trampoline_true, /* create_iomfb_service */
|
||||
[111] = trampoline_create_backlight_service,
|
||||
[116] = dcpep_cb_boot_1,
|
||||
[117] = trampoline_false, /* is_dark_boot */
|
||||
[118] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/
|
||||
[120] = trampoline_read_edt_data,
|
||||
[122] = trampoline_prop_start,
|
||||
[123] = trampoline_prop_chunk,
|
||||
[124] = trampoline_prop_end,
|
||||
[201] = trampoline_map_piodma,
|
||||
[202] = trampoline_unmap_piodma,
|
||||
[206] = iomfbep_cb_match_pmu_service_2,
|
||||
[207] = iomfbep_cb_match_backlight_service,
|
||||
[208] = trampoline_get_time,
|
||||
[211] = trampoline_nop, /* update_backlight_factor_prop */
|
||||
[300] = trampoline_pr_publish,
|
||||
[401] = trampoline_get_uint_prop,
|
||||
[404] = trampoline_nop, /* sr_set_uint_prop */
|
||||
[406] = trampoline_set_fx_prop,
|
||||
[408] = trampoline_get_frequency,
|
||||
[411] = trampoline_map_reg,
|
||||
[413] = trampoline_true, /* sr_set_property_dict */
|
||||
[414] = trampoline_sr_set_property_int,
|
||||
[415] = trampoline_true, /* sr_set_property_bool */
|
||||
[451] = trampoline_allocate_buffer,
|
||||
[452] = trampoline_map_physical,
|
||||
[456] = trampoline_release_mem_desc,
|
||||
[552] = trampoline_true, /* set_property_dict_0 */
|
||||
[561] = trampoline_true, /* set_property_dict */
|
||||
[563] = trampoline_true, /* set_property_int */
|
||||
[565] = trampoline_true, /* set_property_bool */
|
||||
[567] = trampoline_true, /* set_property_str */
|
||||
[574] = trampoline_zero, /* power_up_dart */
|
||||
[576] = trampoline_hotplug,
|
||||
[577] = trampoline_nop, /* powerstate_notify */
|
||||
[582] = trampoline_true, /* create_default_fb_surface */
|
||||
[584] = trampoline_nop, /* IOMobileFramebufferAP::clear_default_surface */
|
||||
[588] = trampoline_nop, /* resize_default_fb_surface_gated */
|
||||
[589] = trampoline_swap_complete,
|
||||
[591] = trampoline_swap_complete_intent_gated,
|
||||
[592] = trampoline_abort_swap_ap_gated,
|
||||
[593] = trampoline_enable_backlight_message_ap_gated,
|
||||
[594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */
|
||||
[596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */
|
||||
[597] = trampoline_false, /* IOMobileFramebufferAP::preserveContents */
|
||||
[598] = trampoline_nop, /* find_swap_function_gated */
|
||||
};
|
||||
|
||||
void DCP_FW_NAME(iomfb_start)(struct apple_dcp *dcp)
|
||||
{
|
||||
dcp->cb_handlers = cb_handlers;
|
||||
|
||||
dcp_start_signal(dcp, false, dcp_started, NULL);
|
||||
}
|
||||
|
||||
#undef DCP_FW_VER
|
||||
#undef DCP_FW
|
17
sys/dev/pci/drm/apple/iomfb_v12_3.h
Normal file
17
sys/dev/pci/drm/apple/iomfb_v12_3.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright The Asahi Linux Contributors */
|
||||
|
||||
#ifndef __APPLE_IOMFB_V12_3_H__
|
||||
#define __APPLE_IOMFB_V12_3_H__
|
||||
|
||||
#include "version_utils.h"
|
||||
|
||||
#define DCP_FW v12_3
|
||||
#define DCP_FW_VER DCP_FW_VERSION(12, 3, 0)
|
||||
|
||||
#include "iomfb_template.h"
|
||||
|
||||
#undef DCP_FW_VER
|
||||
#undef DCP_FW
|
||||
|
||||
#endif /* __APPLE_IOMFB_V12_3_H__ */
|
113
sys/dev/pci/drm/apple/iomfb_v13_3.c
Normal file
113
sys/dev/pci/drm/apple/iomfb_v13_3.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright The Asahi Linux Contributors */
|
||||
|
||||
#include "iomfb_v12_3.h"
|
||||
#include "iomfb_v13_3.h"
|
||||
#include "version_utils.h"
|
||||
|
||||
static const struct dcp_method_entry dcp_methods[dcpep_num_methods] = {
|
||||
IOMFB_METHOD("A000", dcpep_late_init_signal),
|
||||
IOMFB_METHOD("A029", dcpep_setup_video_limits),
|
||||
IOMFB_METHOD("A131", iomfbep_a131_pmu_service_matched),
|
||||
IOMFB_METHOD("A132", iomfbep_a132_backlight_service_matched),
|
||||
IOMFB_METHOD("A373", dcpep_set_create_dfb),
|
||||
IOMFB_METHOD("A374", iomfbep_a358_vi_set_temperature_hint),
|
||||
IOMFB_METHOD("A401", dcpep_start_signal),
|
||||
IOMFB_METHOD("A407", dcpep_swap_start),
|
||||
IOMFB_METHOD("A408", dcpep_swap_submit),
|
||||
IOMFB_METHOD("A410", dcpep_set_display_device),
|
||||
IOMFB_METHOD("A411", dcpep_is_main_display),
|
||||
IOMFB_METHOD("A412", dcpep_set_digital_out_mode),
|
||||
IOMFB_METHOD("A422", iomfbep_set_matrix),
|
||||
IOMFB_METHOD("A426", iomfbep_get_color_remap_mode),
|
||||
IOMFB_METHOD("A441", dcpep_set_parameter_dcp),
|
||||
IOMFB_METHOD("A445", dcpep_create_default_fb),
|
||||
IOMFB_METHOD("A449", dcpep_enable_disable_video_power_savings),
|
||||
IOMFB_METHOD("A456", dcpep_first_client_open),
|
||||
IOMFB_METHOD("A457", iomfbep_last_client_close),
|
||||
IOMFB_METHOD("A463", dcpep_set_display_refresh_properties),
|
||||
IOMFB_METHOD("A466", dcpep_flush_supports_power),
|
||||
IOMFB_METHOD("A467", iomfbep_abort_swaps_dcp),
|
||||
IOMFB_METHOD("A472", dcpep_set_power_state),
|
||||
};
|
||||
|
||||
#define DCP_FW v13_3
|
||||
#define DCP_FW_VER DCP_FW_VERSION(13, 3, 0)
|
||||
|
||||
#include "iomfb_template.c"
|
||||
|
||||
static const iomfb_cb_handler cb_handlers[IOMFB_MAX_CB] = {
|
||||
[0] = trampoline_true, /* did_boot_signal */
|
||||
[1] = trampoline_true, /* did_power_on_signal */
|
||||
[2] = trampoline_nop, /* will_power_off_signal */
|
||||
[3] = trampoline_rt_bandwidth,
|
||||
[6] = trampoline_set_frame_sync_props,
|
||||
[100] = iomfbep_cb_match_pmu_service,
|
||||
[101] = trampoline_zero, /* get_display_default_stride */
|
||||
[102] = trampoline_nop, /* set_number_property */
|
||||
[103] = trampoline_nop, /* trigger_user_cal_loader */
|
||||
[104] = trampoline_nop, /* set_boolean_property */
|
||||
[107] = trampoline_nop, /* remove_property */
|
||||
[108] = trampoline_true, /* create_provider_service */
|
||||
[109] = trampoline_true, /* create_product_service */
|
||||
[110] = trampoline_true, /* create_pmu_service */
|
||||
[111] = trampoline_true, /* create_iomfb_service */
|
||||
[112] = trampoline_create_backlight_service,
|
||||
[113] = trampoline_true, /* create_nvram_servce? */
|
||||
[114] = trampoline_get_tiling_state,
|
||||
[115] = trampoline_false, /* set_tiling_state */
|
||||
[120] = dcpep_cb_boot_1,
|
||||
[121] = trampoline_false, /* is_dark_boot */
|
||||
[122] = trampoline_false, /* is_dark_boot / is_waking_from_hibernate*/
|
||||
[124] = trampoline_read_edt_data,
|
||||
[126] = trampoline_prop_start,
|
||||
[127] = trampoline_prop_chunk,
|
||||
[128] = trampoline_prop_end,
|
||||
[129] = trampoline_allocate_bandwidth,
|
||||
[201] = trampoline_map_piodma,
|
||||
[202] = trampoline_unmap_piodma,
|
||||
[206] = iomfbep_cb_match_pmu_service_2,
|
||||
[207] = iomfbep_cb_match_backlight_service,
|
||||
[208] = trampoline_nop, /* update_backlight_factor_prop */
|
||||
[209] = trampoline_get_time,
|
||||
[300] = trampoline_pr_publish,
|
||||
[401] = trampoline_get_uint_prop,
|
||||
[404] = trampoline_nop, /* sr_set_uint_prop */
|
||||
[406] = trampoline_set_fx_prop,
|
||||
[408] = trampoline_get_frequency,
|
||||
[411] = trampoline_map_reg,
|
||||
[413] = trampoline_true, /* sr_set_property_dict */
|
||||
[414] = trampoline_sr_set_property_int,
|
||||
[415] = trampoline_true, /* sr_set_property_bool */
|
||||
[451] = trampoline_allocate_buffer,
|
||||
[452] = trampoline_map_physical,
|
||||
[456] = trampoline_release_mem_desc,
|
||||
[552] = trampoline_true, /* set_property_dict_0 */
|
||||
[561] = trampoline_true, /* set_property_dict */
|
||||
[563] = trampoline_true, /* set_property_int */
|
||||
[565] = trampoline_true, /* set_property_bool */
|
||||
[567] = trampoline_true, /* set_property_str */
|
||||
[574] = trampoline_zero, /* power_up_dart */
|
||||
[576] = trampoline_hotplug,
|
||||
[577] = trampoline_nop, /* powerstate_notify */
|
||||
[582] = trampoline_true, /* create_default_fb_surface */
|
||||
[584] = trampoline_nop, /* IOMobileFramebufferAP::clear_default_surface */
|
||||
[588] = trampoline_nop, /* resize_default_fb_surface_gated */
|
||||
[589] = trampoline_swap_complete,
|
||||
[591] = trampoline_swap_complete_intent_gated,
|
||||
[592] = trampoline_abort_swap_ap_gated,
|
||||
[593] = trampoline_enable_backlight_message_ap_gated,
|
||||
[594] = trampoline_nop, /* IOMobileFramebufferAP::setSystemConsoleMode */
|
||||
[596] = trampoline_false, /* IOMobileFramebufferAP::isDFBAllocated */
|
||||
[597] = trampoline_false, /* IOMobileFramebufferAP::preserveContents */
|
||||
[598] = trampoline_nop, /* find_swap_function_gated */
|
||||
};
|
||||
void DCP_FW_NAME(iomfb_start)(struct apple_dcp *dcp)
|
||||
{
|
||||
dcp->cb_handlers = cb_handlers;
|
||||
|
||||
dcp_start_signal(dcp, false, dcp_started, NULL);
|
||||
}
|
||||
|
||||
#undef DCP_FW_VER
|
||||
#undef DCP_FW
|
17
sys/dev/pci/drm/apple/iomfb_v13_3.h
Normal file
17
sys/dev/pci/drm/apple/iomfb_v13_3.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright The Asahi Linux Contributors */
|
||||
|
||||
#ifndef __APPLE_IOMFB_V13_3_H__
|
||||
#define __APPLE_IOMFB_V13_3_H__
|
||||
|
||||
#include "version_utils.h"
|
||||
|
||||
#define DCP_FW v13_3
|
||||
#define DCP_FW_VER DCP_FW_VERSION(13, 3, 0)
|
||||
|
||||
#include "iomfb_template.h"
|
||||
|
||||
#undef DCP_FW_VER
|
||||
#undef DCP_FW
|
||||
|
||||
#endif /* __APPLE_IOMFB_V13_3_H__ */
|
1047
sys/dev/pci/drm/apple/parser.c
Normal file
1047
sys/dev/pci/drm/apple/parser.c
Normal file
File diff suppressed because it is too large
Load diff
138
sys/dev/pci/drm/apple/parser.h
Normal file
138
sys/dev/pci/drm/apple/parser.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2021 Alyssa Rosenzweig <alyssa@rosenzweig.io> */
|
||||
|
||||
#ifndef __APPLE_DCP_PARSER_H__
|
||||
#define __APPLE_DCP_PARSER_H__
|
||||
|
||||
/* For mode parsing */
|
||||
#include <drm/drm_modes.h>
|
||||
|
||||
struct apple_dcp;
|
||||
|
||||
struct dcp_parse_ctx {
|
||||
struct apple_dcp *dcp;
|
||||
const void *blob;
|
||||
u32 pos, len;
|
||||
};
|
||||
|
||||
enum dcp_color_eotf {
|
||||
DCP_EOTF_SDR_GAMMA = 0, // "SDR gamma"
|
||||
DCP_EOTF_HDR_GAMMA = 1, // "HDR gamma"
|
||||
DCP_EOTF_ST_2084 = 2, // "ST 2084 (PQ)"
|
||||
DCP_EOTF_BT_2100 = 3, // "BT.2100 (HLG)"
|
||||
DCP_EOTF_COUNT
|
||||
};
|
||||
|
||||
enum dcp_color_format {
|
||||
DCP_COLOR_FORMAT_RGB = 0, // "RGB"
|
||||
DCP_COLOR_FORMAT_YCBCR420 = 1, // "YUV 4:2:0"
|
||||
DCP_COLOR_FORMAT_YCBCR422 = 3, // "YUV 4:2:2"
|
||||
DCP_COLOR_FORMAT_YCBCR444 = 2, // "YUV 4:4:4"
|
||||
DCP_COLOR_FORMAT_DV_NATIVE = 4, // "DolbyVision (native)"
|
||||
DCP_COLOR_FORMAT_DV_HDMI = 5, // "DolbyVision (HDMI)"
|
||||
DCP_COLOR_FORMAT_YCBCR422_DP = 6, // "YCbCr 4:2:2 (DP tunnel)"
|
||||
DCP_COLOR_FORMAT_YCBCR422_HDMI = 7, // "YCbCr 4:2:2 (HDMI tunnel)"
|
||||
DCP_COLOR_FORMAT_DV_LL_YCBCR422 = 8, // "DolbyVision LL YCbCr 4:2:2"
|
||||
DCP_COLOR_FORMAT_DV_LL_YCBCR422_DP = 9, // "DolbyVision LL YCbCr 4:2:2 (DP)"
|
||||
DCP_COLOR_FORMAT_DV_LL_YCBCR422_HDMI = 10, // "DolbyVision LL YCbCr 4:2:2 (HDMI)"
|
||||
DCP_COLOR_FORMAT_DV_LL_YCBCR444 = 11, // "DolbyVision LL YCbCr 4:4:4"
|
||||
DCP_COLOR_FORMAT_DV_LL_RGB422 = 12, // "DolbyVision LL RGB 4:2:2"
|
||||
DCP_COLOR_FORMAT_GRGB_BLUE_422 = 13, // "GRGB as YCbCr422 (Even line blue)"
|
||||
DCP_COLOR_FORMAT_GRGB_RED_422 = 14, // "GRGB as YCbCr422 (Even line red)"
|
||||
DCP_COLOR_FORMAT_COUNT
|
||||
};
|
||||
|
||||
enum dcp_colorimetry {
|
||||
DCP_COLORIMETRY_BT601 = 0, // "SMPTE 170M/BT.601"
|
||||
DCP_COLORIMETRY_BT709 = 1, // "BT.701"
|
||||
DCP_COLORIMETRY_XVYCC_601 = 2, // "xvYCC601"
|
||||
DCP_COLORIMETRY_XVYCC_709 = 3, // "xvYCC709"
|
||||
DCP_COLORIMETRY_SYCC_601 = 4, // "sYCC601"
|
||||
DCP_COLORIMETRY_ADOBE_YCC_601 = 5, // "AdobeYCC601"
|
||||
DCP_COLORIMETRY_BT2020_CYCC = 6, // "BT.2020 (c)"
|
||||
DCP_COLORIMETRY_BT2020_YCC = 7, // "BT.2020 (nc)"
|
||||
DCP_COLORIMETRY_VSVDB = 8, // "DolbyVision VSVDB"
|
||||
DCP_COLORIMETRY_BT2020_RGB = 9, // "BT.2020 (RGB)"
|
||||
DCP_COLORIMETRY_SRGB = 10, // "sRGB"
|
||||
DCP_COLORIMETRY_SCRGB = 11, // "scRGB"
|
||||
DCP_COLORIMETRY_SCRGB_FIXED = 12, // "scRGBfixed"
|
||||
DCP_COLORIMETRY_ADOBE_RGB = 13, // "AdobeRGB"
|
||||
DCP_COLORIMETRY_DCI_P3_RGB_D65 = 14, // "DCI-P3 (D65)"
|
||||
DCP_COLORIMETRY_DCI_P3_RGB_THEATER = 15, // "DCI-P3 (Theater)"
|
||||
DCP_COLORIMETRY_RGB = 16, // "Default RGB"
|
||||
DCP_COLORIMETRY_COUNT
|
||||
};
|
||||
|
||||
enum dcp_color_range {
|
||||
DCP_COLOR_YCBCR_RANGE_FULL = 0,
|
||||
DCP_COLOR_YCBCR_RANGE_LIMITED = 1,
|
||||
DCP_COLOR_YCBCR_RANGE_COUNT
|
||||
};
|
||||
|
||||
struct dcp_color_mode {
|
||||
s64 score;
|
||||
u32 id;
|
||||
enum dcp_color_eotf eotf;
|
||||
enum dcp_color_format format;
|
||||
enum dcp_colorimetry colorimetry;
|
||||
enum dcp_color_range range;
|
||||
u8 depth;
|
||||
};
|
||||
|
||||
/*
|
||||
* Represents a single display mode. These mode objects are populated at
|
||||
* runtime based on the TimingElements dictionary sent by the DCP.
|
||||
*/
|
||||
struct dcp_display_mode {
|
||||
struct drm_display_mode mode;
|
||||
u32 color_mode_id;
|
||||
u32 timing_mode_id;
|
||||
struct dcp_color_mode sdr_rgb;
|
||||
struct dcp_color_mode sdr_444;
|
||||
struct dcp_color_mode sdr;
|
||||
struct dcp_color_mode best;
|
||||
};
|
||||
|
||||
struct dimension {
|
||||
s64 total, front_porch, sync_width, active;
|
||||
s64 precise_sync_rate;
|
||||
};
|
||||
|
||||
int parse(const void *blob, size_t size, struct dcp_parse_ctx *ctx);
|
||||
struct dcp_display_mode *enumerate_modes(struct dcp_parse_ctx *handle,
|
||||
unsigned int *count, int width_mm,
|
||||
int height_mm, unsigned notch_height);
|
||||
int parse_display_attributes(struct dcp_parse_ctx *handle, int *width_mm,
|
||||
int *height_mm);
|
||||
int parse_epic_service_init(struct dcp_parse_ctx *handle, const char **name,
|
||||
const char **class, s64 *unit);
|
||||
|
||||
struct dcp_sound_format_mask {
|
||||
u64 formats; /* SNDRV_PCM_FMTBIT_* */
|
||||
unsigned int rates; /* SNDRV_PCM_RATE_* */
|
||||
unsigned int nchans;
|
||||
};
|
||||
|
||||
struct dcp_sound_cookie {
|
||||
u8 data[24];
|
||||
};
|
||||
|
||||
struct snd_pcm_chmap_elem;
|
||||
int parse_sound_constraints(struct dcp_parse_ctx *handle,
|
||||
struct dcp_sound_format_mask *sieve,
|
||||
struct dcp_sound_format_mask *hits);
|
||||
int parse_sound_mode(struct dcp_parse_ctx *handle,
|
||||
struct dcp_sound_format_mask *sieve,
|
||||
struct snd_pcm_chmap_elem *chmap,
|
||||
struct dcp_sound_cookie *cookie);
|
||||
|
||||
struct dcp_system_ev_mnits {
|
||||
u32 timestamp;
|
||||
u32 millinits;
|
||||
u32 idac;
|
||||
};
|
||||
|
||||
int parse_system_log_mnits(struct dcp_parse_ctx *handle,
|
||||
struct dcp_system_ev_mnits *entry);
|
||||
|
||||
#endif
|
137
sys/dev/pci/drm/apple/systemep.c
Normal file
137
sys/dev/pci/drm/apple/systemep.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2022 Sven Peter <sven@svenpeter.dev> */
|
||||
|
||||
#include <linux/completion.h>
|
||||
|
||||
#include "afk.h"
|
||||
#include "dcp.h"
|
||||
#include "parser.h"
|
||||
|
||||
static bool enable_verbose_logging;
|
||||
module_param(enable_verbose_logging, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_verbose_logging, "Enable DCP firmware verbose logging");
|
||||
|
||||
/*
|
||||
* Serialized setProperty("gAFKConfigLogMask", 0xffff) IPC call which
|
||||
* will set the DCP firmware log level to the most verbose setting
|
||||
*/
|
||||
#define SYSTEM_SET_PROPERTY 0x43
|
||||
static const u8 setprop_gAFKConfigLogMask_ffff[] = {
|
||||
0x14, 0x00, 0x00, 0x00, 0x67, 0x41, 0x46, 0x4b, 0x43, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x4c, 0x6f, 0x67, 0x4d, 0x61, 0x73,
|
||||
0x6b, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x00, 0x84, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
struct systemep_work {
|
||||
struct apple_epic_service *service;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
static void system_log_work(struct work_struct *work_)
|
||||
{
|
||||
struct systemep_work *work =
|
||||
container_of(work_, struct systemep_work, work);
|
||||
|
||||
afk_send_command(work->service, SYSTEM_SET_PROPERTY,
|
||||
setprop_gAFKConfigLogMask_ffff,
|
||||
sizeof(setprop_gAFKConfigLogMask_ffff), NULL,
|
||||
sizeof(setprop_gAFKConfigLogMask_ffff), NULL);
|
||||
complete(&work->service->ep->dcp->systemep_done);
|
||||
kfree(work);
|
||||
}
|
||||
|
||||
static void system_init(struct apple_epic_service *service, const char *name,
|
||||
const char *class, s64 unit)
|
||||
{
|
||||
struct systemep_work *work;
|
||||
|
||||
if (!enable_verbose_logging)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We're called from the service message handler thread and can't
|
||||
* dispatch blocking message from there.
|
||||
*/
|
||||
work = kzalloc(sizeof(*work), GFP_KERNEL);
|
||||
if (!work)
|
||||
return;
|
||||
|
||||
work->service = service;
|
||||
INIT_WORK(&work->work, system_log_work);
|
||||
schedule_work(&work->work);
|
||||
}
|
||||
|
||||
static void powerlog_init(struct apple_epic_service *service, const char *name,
|
||||
const char *class, s64 unit)
|
||||
{
|
||||
}
|
||||
|
||||
static int powerlog_report(struct apple_epic_service *service, enum epic_subtype type,
|
||||
const void *data, size_t data_size)
|
||||
{
|
||||
struct dcp_system_ev_mnits mnits;
|
||||
struct dcp_parse_ctx parse_ctx;
|
||||
struct apple_dcp *dcp = service->ep->dcp;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dcp->dev, "systemep[ch:%u]: report type:%02x len:%zu\n",
|
||||
service->channel, type, data_size);
|
||||
|
||||
if (type != EPIC_SUBTYPE_STD_SERVICE)
|
||||
return 0;
|
||||
|
||||
ret = parse(data, data_size, &parse_ctx);
|
||||
if (ret) {
|
||||
dev_warn(service->ep->dcp->dev, "systemep: failed to parse report: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = parse_system_log_mnits(&parse_ctx, &mnits);
|
||||
if (ret) {
|
||||
/* ignore parse errors in the case dcp sends unknown log events */
|
||||
dev_dbg(dcp->dev, "systemep: failed to parse mNits event: %d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(dcp->dev, "systemep: mNits event: Nits: %u.%03u, iDAC: %u\n",
|
||||
mnits.millinits / 1000, mnits.millinits % 1000, mnits.idac);
|
||||
|
||||
dcp->brightness.nits = mnits.millinits / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct apple_epic_service_ops systemep_ops[] = {
|
||||
{
|
||||
.name = "system",
|
||||
.init = system_init,
|
||||
},
|
||||
{
|
||||
.name = "powerlog-service",
|
||||
.init = powerlog_init,
|
||||
.report = powerlog_report,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
int systemep_init(struct apple_dcp *dcp)
|
||||
{
|
||||
init_completion(&dcp->systemep_done);
|
||||
|
||||
dcp->systemep = afk_init(dcp, SYSTEM_ENDPOINT, systemep_ops);
|
||||
afk_start(dcp->systemep);
|
||||
|
||||
if (!enable_verbose_logging)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Timeouts aren't really fatal here: in the worst case we just weren't
|
||||
* able to enable additional debug prints inside DCP
|
||||
*/
|
||||
if (!wait_for_completion_timeout(&dcp->systemep_done,
|
||||
msecs_to_jiffies(MSEC_PER_SEC)))
|
||||
dev_err(dcp->dev, "systemep: couldn't enable verbose logs\n");
|
||||
|
||||
return 0;
|
||||
}
|
9
sys/dev/pci/drm/apple/trace.c
Normal file
9
sys/dev/pci/drm/apple/trace.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+ OR MIT
|
||||
/*
|
||||
* Tracepoints for Apple DCP driver
|
||||
*
|
||||
* Copyright (C) The Asahi Linux Contributors
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
596
sys/dev/pci/drm/apple/trace.h
Normal file
596
sys/dev/pci/drm/apple/trace.h
Normal file
|
@ -0,0 +1,596 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright (C) The Asahi Linux Contributors */
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM dcp
|
||||
|
||||
#if !defined(_TRACE_DCP_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_DCP_H
|
||||
|
||||
#include "afk.h"
|
||||
#include "dptxep.h"
|
||||
#include "dcp-internal.h"
|
||||
#include "parser.h"
|
||||
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#define show_dcp_endpoint(ep) \
|
||||
__print_symbolic(ep, { SYSTEM_ENDPOINT, "system" }, \
|
||||
{ TEST_ENDPOINT, "test" }, \
|
||||
{ DCP_EXPERT_ENDPOINT, "dcpexpert" }, \
|
||||
{ DISP0_ENDPOINT, "disp0" }, \
|
||||
{ DPTX_ENDPOINT, "dptxport" }, \
|
||||
{ HDCP_ENDPOINT, "hdcp" }, \
|
||||
{ REMOTE_ALLOC_ENDPOINT, "remotealloc" }, \
|
||||
{ IOMFB_ENDPOINT, "iomfb" })
|
||||
#define print_epic_type(etype) \
|
||||
__print_symbolic(etype, { EPIC_TYPE_NOTIFY, "notify" }, \
|
||||
{ EPIC_TYPE_COMMAND, "command" }, \
|
||||
{ EPIC_TYPE_REPLY, "reply" }, \
|
||||
{ EPIC_TYPE_NOTIFY_ACK, "notify-ack" })
|
||||
|
||||
#define print_epic_category(ecat) \
|
||||
__print_symbolic(ecat, { EPIC_CAT_REPORT, "report" }, \
|
||||
{ EPIC_CAT_NOTIFY, "notify" }, \
|
||||
{ EPIC_CAT_REPLY, "reply" }, \
|
||||
{ EPIC_CAT_COMMAND, "command" })
|
||||
|
||||
#define show_dptxport_apcall(idx) \
|
||||
__print_symbolic( \
|
||||
idx, { DPTX_APCALL_ACTIVATE, "activate" }, \
|
||||
{ DPTX_APCALL_DEACTIVATE, "deactivate" }, \
|
||||
{ DPTX_APCALL_GET_MAX_DRIVE_SETTINGS, \
|
||||
"get_max_drive_settings" }, \
|
||||
{ DPTX_APCALL_SET_DRIVE_SETTINGS, "set_drive_settings" }, \
|
||||
{ DPTX_APCALL_GET_DRIVE_SETTINGS, "get_drive_settings" }, \
|
||||
{ DPTX_APCALL_WILL_CHANGE_LINKG_CONFIG, \
|
||||
"will_change_link_config" }, \
|
||||
{ DPTX_APCALL_DID_CHANGE_LINK_CONFIG, \
|
||||
"did_change_link_config" }, \
|
||||
{ DPTX_APCALL_GET_MAX_LINK_RATE, "get_max_link_rate" }, \
|
||||
{ DPTX_APCALL_GET_LINK_RATE, "get_link_rate" }, \
|
||||
{ DPTX_APCALL_SET_LINK_RATE, "set_link_rate" }, \
|
||||
{ DPTX_APCALL_GET_MAX_LANE_COUNT, \
|
||||
"get_max_lane_count" }, \
|
||||
{ DPTX_APCALL_GET_ACTIVE_LANE_COUNT, \
|
||||
"get_active_lane_count" }, \
|
||||
{ DPTX_APCALL_SET_ACTIVE_LANE_COUNT, \
|
||||
"set_active_lane_count" }, \
|
||||
{ DPTX_APCALL_GET_SUPPORTS_DOWN_SPREAD, \
|
||||
"get_supports_downspread" }, \
|
||||
{ DPTX_APCALL_GET_DOWN_SPREAD, "get_downspread" }, \
|
||||
{ DPTX_APCALL_SET_DOWN_SPREAD, "set_downspread" }, \
|
||||
{ DPTX_APCALL_GET_SUPPORTS_LANE_MAPPING, \
|
||||
"get_supports_lane_mapping" }, \
|
||||
{ DPTX_APCALL_SET_LANE_MAP, "set_lane_map" }, \
|
||||
{ DPTX_APCALL_GET_SUPPORTS_HPD, "get_supports_hpd" }, \
|
||||
{ DPTX_APCALL_FORCE_HOTPLUG_DETECT, "force_hotplug_detect" }, \
|
||||
{ DPTX_APCALL_INACTIVE_SINK_DETECTED, \
|
||||
"inactive_sink_detected" }, \
|
||||
{ DPTX_APCALL_SET_TILED_DISPLAY_HINTS, \
|
||||
"set_tiled_display_hints" }, \
|
||||
{ DPTX_APCALL_DEVICE_NOT_RESPONDING, \
|
||||
"device_not_responding" }, \
|
||||
{ DPTX_APCALL_DEVICE_BUSY_TIMEOUT, "device_busy_timeout" }, \
|
||||
{ DPTX_APCALL_DEVICE_NOT_STARTED, "device_not_started" })
|
||||
|
||||
TRACE_EVENT(dcp_recv_msg,
|
||||
TP_PROTO(struct apple_dcp *dcp, u8 endpoint, u64 message),
|
||||
TP_ARGS(dcp, endpoint, message),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(dcp->dev))
|
||||
__field(u8, endpoint)
|
||||
__field(u64, message)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(dcp->dev));
|
||||
__entry->endpoint = endpoint;
|
||||
__entry->message = message;),
|
||||
|
||||
TP_printk("%s: endpoint 0x%x (%s): received message 0x%016llx",
|
||||
__get_str(devname), __entry->endpoint,
|
||||
show_dcp_endpoint(__entry->endpoint), __entry->message));
|
||||
|
||||
TRACE_EVENT(dcp_send_msg,
|
||||
TP_PROTO(struct apple_dcp *dcp, u8 endpoint, u64 message),
|
||||
TP_ARGS(dcp, endpoint, message),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(dcp->dev))
|
||||
__field(u8, endpoint)
|
||||
__field(u64, message)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(dcp->dev));
|
||||
__entry->endpoint = endpoint;
|
||||
__entry->message = message;),
|
||||
|
||||
TP_printk("%s: endpoint 0x%x (%s): will send message 0x%016llx",
|
||||
__get_str(devname), __entry->endpoint,
|
||||
show_dcp_endpoint(__entry->endpoint), __entry->message));
|
||||
|
||||
TRACE_EVENT(
|
||||
afk_getbuf, TP_PROTO(struct apple_dcp_afkep *ep, u16 size, u16 tag),
|
||||
TP_ARGS(ep, size, tag),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev))
|
||||
__field(u8, endpoint) __field(u16, size)
|
||||
__field(u16, tag)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev));
|
||||
__entry->endpoint = ep->endpoint; __entry->size = size;
|
||||
__entry->tag = tag;),
|
||||
|
||||
TP_printk(
|
||||
"%s: endpoint 0x%x (%s): get buffer with size 0x%x and tag 0x%x",
|
||||
__get_str(devname), __entry->endpoint,
|
||||
show_dcp_endpoint(__entry->endpoint), __entry->size,
|
||||
__entry->tag));
|
||||
|
||||
DECLARE_EVENT_CLASS(afk_rwptr_template,
|
||||
TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr),
|
||||
TP_ARGS(ep, rptr, wptr),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev))
|
||||
__field(u8, endpoint) __field(u32, rptr)
|
||||
__field(u32, wptr)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev));
|
||||
__entry->endpoint = ep->endpoint;
|
||||
__entry->rptr = rptr; __entry->wptr = wptr;),
|
||||
|
||||
TP_printk("%s: endpoint 0x%x (%s): rptr 0x%x, wptr 0x%x",
|
||||
__get_str(devname), __entry->endpoint,
|
||||
show_dcp_endpoint(__entry->endpoint), __entry->rptr,
|
||||
__entry->wptr));
|
||||
|
||||
DEFINE_EVENT(afk_rwptr_template, afk_recv_rwptr_pre,
|
||||
TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr),
|
||||
TP_ARGS(ep, rptr, wptr));
|
||||
DEFINE_EVENT(afk_rwptr_template, afk_recv_rwptr_post,
|
||||
TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr),
|
||||
TP_ARGS(ep, rptr, wptr));
|
||||
DEFINE_EVENT(afk_rwptr_template, afk_send_rwptr_pre,
|
||||
TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr),
|
||||
TP_ARGS(ep, rptr, wptr));
|
||||
DEFINE_EVENT(afk_rwptr_template, afk_send_rwptr_post,
|
||||
TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 wptr),
|
||||
TP_ARGS(ep, rptr, wptr));
|
||||
|
||||
TRACE_EVENT(
|
||||
afk_recv_qe,
|
||||
TP_PROTO(struct apple_dcp_afkep *ep, u32 rptr, u32 magic, u32 size),
|
||||
TP_ARGS(ep, rptr, magic, size),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev))
|
||||
__field(u8, endpoint) __field(u32, rptr)
|
||||
__field(u32, magic)
|
||||
__field(u32, size)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev));
|
||||
__entry->endpoint = ep->endpoint; __entry->rptr = rptr;
|
||||
__entry->magic = magic; __entry->size = size;),
|
||||
|
||||
TP_printk("%s: endpoint 0x%x (%s): QE rptr 0x%x, magic 0x%x, size 0x%x",
|
||||
__get_str(devname), __entry->endpoint,
|
||||
show_dcp_endpoint(__entry->endpoint), __entry->rptr,
|
||||
__entry->magic, __entry->size));
|
||||
|
||||
TRACE_EVENT(
|
||||
afk_recv_handle,
|
||||
TP_PROTO(struct apple_dcp_afkep *ep, u32 channel, u32 type,
|
||||
u32 data_size, struct epic_hdr *ehdr,
|
||||
struct epic_sub_hdr *eshdr),
|
||||
TP_ARGS(ep, channel, type, data_size, ehdr, eshdr),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(ep->dcp->dev)) __field(
|
||||
u8, endpoint) __field(u32, channel) __field(u32, type)
|
||||
__field(u32, data_size) __field(u8, category)
|
||||
__field(u16, subtype)
|
||||
__field(u16, tag)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(ep->dcp->dev));
|
||||
__entry->endpoint = ep->endpoint;
|
||||
__entry->channel = channel; __entry->type = type;
|
||||
__entry->data_size = data_size;
|
||||
__entry->category = eshdr->category,
|
||||
__entry->subtype = le16_to_cpu(eshdr->type),
|
||||
__entry->tag = le16_to_cpu(eshdr->tag)),
|
||||
|
||||
TP_printk(
|
||||
"%s: endpoint 0x%x (%s): channel 0x%x, type 0x%x (%s), data_size 0x%x, category: 0x%x (%s), subtype: 0x%x, seq: 0x%x",
|
||||
__get_str(devname), __entry->endpoint,
|
||||
show_dcp_endpoint(__entry->endpoint), __entry->channel,
|
||||
__entry->type, print_epic_type(__entry->type),
|
||||
__entry->data_size, __entry->category,
|
||||
print_epic_category(__entry->category), __entry->subtype,
|
||||
__entry->tag));
|
||||
|
||||
TRACE_EVENT(iomfb_callback,
|
||||
TP_PROTO(struct apple_dcp *dcp, int tag, const char *name),
|
||||
TP_ARGS(dcp, tag, name),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(devname, dev_name(dcp->dev))
|
||||
__field(int, tag)
|
||||
__field(const char *, name)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(devname, dev_name(dcp->dev));
|
||||
__entry->tag = tag; __entry->name = name;
|
||||
),
|
||||
|
||||
TP_printk("%s: Callback D%03d %s", __get_str(devname), __entry->tag,
|
||||
__entry->name));
|
||||
|
||||
TRACE_EVENT(iomfb_push,
|
||||
TP_PROTO(struct apple_dcp *dcp,
|
||||
const struct dcp_method_entry *method, int context,
|
||||
int offset, int depth),
|
||||
TP_ARGS(dcp, method, context, offset, depth),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__string(devname, dev_name(dcp->dev))
|
||||
__string(name, method->name)
|
||||
__field(int, context)
|
||||
__field(int, offset)
|
||||
__field(int, depth)),
|
||||
|
||||
TP_fast_assign(
|
||||
__assign_str(devname, dev_name(dcp->dev));
|
||||
__assign_str(name, method->name);
|
||||
__entry->context = context; __entry->offset = offset;
|
||||
__entry->depth = depth;
|
||||
),
|
||||
|
||||
TP_printk("%s: Method %s: context %u, offset %u, depth %u",
|
||||
__get_str(devname), __get_str(name), __entry->context,
|
||||
__entry->offset, __entry->depth));
|
||||
|
||||
TRACE_EVENT(iomfb_swap_submit,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 swap_id),
|
||||
TP_ARGS(dcp, swap_id),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, swap_id)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->swap_id = swap_id;
|
||||
),
|
||||
TP_printk("dcp=%llx, swap_id=%d",
|
||||
__entry->dcp,
|
||||
__entry->swap_id)
|
||||
);
|
||||
|
||||
TRACE_EVENT(iomfb_swap_complete,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 swap_id),
|
||||
TP_ARGS(dcp, swap_id),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, swap_id)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->swap_id = swap_id;
|
||||
),
|
||||
TP_printk("dcp=%llx, swap_id=%d",
|
||||
__entry->dcp,
|
||||
__entry->swap_id
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(iomfb_swap_complete_intent_gated,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 swap_id, u32 width, u32 height),
|
||||
TP_ARGS(dcp, swap_id, width, height),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, swap_id)
|
||||
__field(u32, width)
|
||||
__field(u32, height)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->swap_id = swap_id;
|
||||
__entry->height = height;
|
||||
__entry->width = width;
|
||||
),
|
||||
TP_printk("dcp=%llx, swap_id=%u %ux%u",
|
||||
__entry->dcp,
|
||||
__entry->swap_id,
|
||||
__entry->width,
|
||||
__entry->height
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(iomfb_abort_swap_ap_gated,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 swap_id),
|
||||
TP_ARGS(dcp, swap_id),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, swap_id)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->swap_id = swap_id;
|
||||
),
|
||||
TP_printk("dcp=%llx, swap_id=%u",
|
||||
__entry->dcp,
|
||||
__entry->swap_id
|
||||
)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(iomfb_parse_mode_template,
|
||||
TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score),
|
||||
TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score),
|
||||
|
||||
TP_STRUCT__entry(__field(s64, id)
|
||||
__field_struct(struct dimension, horiz)
|
||||
__field_struct(struct dimension, vert)
|
||||
__field(s64, best_color_mode)
|
||||
__field(bool, is_virtual)
|
||||
__field(s64, score)),
|
||||
|
||||
TP_fast_assign(__entry->id = id;
|
||||
__entry->horiz = *horiz;
|
||||
__entry->vert = *vert;
|
||||
__entry->best_color_mode = best_color_mode;
|
||||
__entry->is_virtual = is_virtual;
|
||||
__entry->score = score;),
|
||||
|
||||
TP_printk("id: %lld, best_color_mode: %lld, resolution:%lldx%lld virtual: %d, score: %lld",
|
||||
__entry->id, __entry->best_color_mode,
|
||||
__entry->horiz.active, __entry->vert.active,
|
||||
__entry->is_virtual, __entry->score));
|
||||
|
||||
DEFINE_EVENT(iomfb_parse_mode_template, iomfb_parse_mode_success,
|
||||
TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score),
|
||||
TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score));
|
||||
|
||||
DEFINE_EVENT(iomfb_parse_mode_template, iomfb_parse_mode_fail,
|
||||
TP_PROTO(s64 id, struct dimension *horiz, struct dimension *vert, s64 best_color_mode, bool is_virtual, s64 score),
|
||||
TP_ARGS(id, horiz, vert, best_color_mode, is_virtual, score));
|
||||
|
||||
TRACE_EVENT(dptxport_init, TP_PROTO(struct apple_dcp *dcp, u64 unit),
|
||||
TP_ARGS(dcp, unit),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(dcp->dev))
|
||||
__field(u64, unit)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(dcp->dev));
|
||||
__entry->unit = unit;),
|
||||
|
||||
TP_printk("%s: dptxport unit %lld initialized", __get_str(devname),
|
||||
__entry->unit));
|
||||
|
||||
TRACE_EVENT(
|
||||
dptxport_apcall,
|
||||
TP_PROTO(struct dptx_port *dptx, int idx, size_t len),
|
||||
TP_ARGS(dptx, idx, len),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev))
|
||||
__field(u32, unit) __field(int, idx) __field(size_t, len)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev));
|
||||
__entry->unit = dptx->unit; __entry->idx = idx; __entry->len = len;),
|
||||
|
||||
TP_printk("%s: dptx%d: AP Call %d (%s) with len %lu", __get_str(devname),
|
||||
__entry->unit,
|
||||
__entry->idx, show_dptxport_apcall(__entry->idx), __entry->len));
|
||||
|
||||
TRACE_EVENT(
|
||||
dptxport_validate_connection,
|
||||
TP_PROTO(struct dptx_port *dptx, u8 core, u8 atc, u8 die),
|
||||
TP_ARGS(dptx, core, atc, die),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev))
|
||||
__field(u32, unit) __field(u8, core) __field(u8, atc) __field(u8, die)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev));
|
||||
__entry->unit = dptx->unit; __entry->core = core; __entry->atc = atc; __entry->die = die;),
|
||||
|
||||
TP_printk("%s: dptx%d: core %d, atc %d, die %d", __get_str(devname),
|
||||
__entry->unit, __entry->core, __entry->atc, __entry->die));
|
||||
|
||||
TRACE_EVENT(
|
||||
dptxport_connect,
|
||||
TP_PROTO(struct dptx_port *dptx, u8 core, u8 atc, u8 die),
|
||||
TP_ARGS(dptx, core, atc, die),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev))
|
||||
__field(u32, unit) __field(u8, core) __field(u8, atc) __field(u8, die)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev));
|
||||
__entry->unit = dptx->unit; __entry->core = core; __entry->atc = atc; __entry->die = die;),
|
||||
|
||||
TP_printk("%s: dptx%d: core %d, atc %d, die %d", __get_str(devname),
|
||||
__entry->unit, __entry->core, __entry->atc, __entry->die));
|
||||
|
||||
TRACE_EVENT(
|
||||
dptxport_call_set_link_rate,
|
||||
TP_PROTO(struct dptx_port *dptx, u32 link_rate),
|
||||
TP_ARGS(dptx, link_rate),
|
||||
|
||||
TP_STRUCT__entry(__string(devname, dev_name(dptx->service->ep->dcp->dev))
|
||||
__field(u32, unit)
|
||||
__field(u32, link_rate)),
|
||||
|
||||
TP_fast_assign(__assign_str(devname, dev_name(dptx->service->ep->dcp->dev));
|
||||
__entry->unit = dptx->unit;
|
||||
__entry->link_rate = link_rate;),
|
||||
|
||||
TP_printk("%s: dptx%d: link rate 0x%x", __get_str(devname), __entry->unit,
|
||||
__entry->link_rate));
|
||||
|
||||
TRACE_EVENT(iomfb_brightness,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 nits),
|
||||
TP_ARGS(dcp, nits),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, nits)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->nits = nits;
|
||||
),
|
||||
TP_printk("dcp=%llx, nits=%u (raw=0x%05x)",
|
||||
__entry->dcp,
|
||||
__entry->nits >> 16,
|
||||
__entry->nits
|
||||
)
|
||||
);
|
||||
|
||||
#define show_eotf(eotf) \
|
||||
__print_symbolic(eotf, { 0, "SDR gamma"}, \
|
||||
{ 1, "HDR gamma"}, \
|
||||
{ 2, "ST 2084 (PQ)"}, \
|
||||
{ 3, "BT.2100 (HLG)"}, \
|
||||
{ 4, "unexpected"})
|
||||
|
||||
#define show_encoding(enc) \
|
||||
__print_symbolic(enc, { 0, "RGB"}, \
|
||||
{ 1, "YUV 4:2:0"}, \
|
||||
{ 3, "YUV 4:2:2"}, \
|
||||
{ 2, "YUV 4:4:4"}, \
|
||||
{ 4, "DolbyVision (native)"}, \
|
||||
{ 5, "DolbyVision (HDMI)"}, \
|
||||
{ 6, "YCbCr 4:2:2 (DP tunnel)"}, \
|
||||
{ 7, "YCbCr 4:2:2 (HDMI tunnel)"}, \
|
||||
{ 8, "DolbyVision LL YCbCr 4:2:2"}, \
|
||||
{ 9, "DolbyVision LL YCbCr 4:2:2 (DP)"}, \
|
||||
{10, "DolbyVision LL YCbCr 4:2:2 (HDMI)"}, \
|
||||
{11, "DolbyVision LL YCbCr 4:4:4"}, \
|
||||
{12, "DolbyVision LL RGB 4:2:2"}, \
|
||||
{13, "GRGB as YCbCr422 (Even line blue)"}, \
|
||||
{14, "GRGB as YCbCr422 (Even line red)"}, \
|
||||
{15, "unexpected"})
|
||||
|
||||
#define show_colorimetry(col) \
|
||||
__print_symbolic(col, { 0, "SMPTE 170M/BT.601"}, \
|
||||
{ 1, "BT.701"}, \
|
||||
{ 2, "xvYCC601"}, \
|
||||
{ 3, "xvYCC709"}, \
|
||||
{ 4, "sYCC601"}, \
|
||||
{ 5, "AdobeYCC601"}, \
|
||||
{ 6, "BT.2020 (c)"}, \
|
||||
{ 7, "BT.2020 (nc)"}, \
|
||||
{ 8, "DolbyVision VSVDB"}, \
|
||||
{ 9, "BT.2020 (RGB)"}, \
|
||||
{10, "sRGB"}, \
|
||||
{11, "scRGB"}, \
|
||||
{12, "scRGBfixed"}, \
|
||||
{13, "AdobeRGB"}, \
|
||||
{14, "DCI-P3 (D65)"}, \
|
||||
{15, "DCI-P3 (Theater)"}, \
|
||||
{16, "Default RGB"}, \
|
||||
{17, "unexpected"})
|
||||
|
||||
#define show_range(range) \
|
||||
__print_symbolic(range, { 0, "Full"}, \
|
||||
{ 1, "Limited"}, \
|
||||
{ 2, "unexpected"})
|
||||
|
||||
TRACE_EVENT(iomfb_color_mode,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 id, u32 score, u32 depth,
|
||||
u32 colorimetry, u32 eotf, u32 range, u32 pixel_enc),
|
||||
TP_ARGS(dcp, id, score, depth, colorimetry, eotf, range, pixel_enc),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, id)
|
||||
__field(u32, score)
|
||||
__field(u32, depth)
|
||||
__field(u32, colorimetry)
|
||||
__field(u32, eotf)
|
||||
__field(u32, range)
|
||||
__field(u32, pixel_enc)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->id = id;
|
||||
__entry->score = score;
|
||||
__entry->depth = depth;
|
||||
__entry->colorimetry = min_t(u32, colorimetry, 17U);
|
||||
__entry->eotf = min_t(u32, eotf, 4U);
|
||||
__entry->range = min_t(u32, range, 2U);
|
||||
__entry->pixel_enc = min_t(u32, pixel_enc, 15U);
|
||||
),
|
||||
TP_printk("dcp=%llx, id=%u, score=%u, depth=%u, colorimetry=%s, eotf=%s, range=%s, pixel_enc=%s",
|
||||
__entry->dcp,
|
||||
__entry->id,
|
||||
__entry->score,
|
||||
__entry->depth,
|
||||
show_colorimetry(__entry->colorimetry),
|
||||
show_eotf(__entry->eotf),
|
||||
show_range(__entry->range),
|
||||
show_encoding(__entry->pixel_enc)
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(iomfb_timing_mode,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 id, u32 score, u32 width,
|
||||
u32 height, u32 clock, u32 color_mode),
|
||||
TP_ARGS(dcp, id, score, width, height, clock, color_mode),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, id)
|
||||
__field(u32, score)
|
||||
__field(u32, width)
|
||||
__field(u32, height)
|
||||
__field(u32, clock)
|
||||
__field(u32, color_mode)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->id = id;
|
||||
__entry->score = score;
|
||||
__entry->width = width;
|
||||
__entry->height = height;
|
||||
__entry->clock = clock;
|
||||
__entry->color_mode = color_mode;
|
||||
),
|
||||
TP_printk("dcp=%llx, id=%u, score=%u, %ux%u@%u.%u, color_mode=%u",
|
||||
__entry->dcp,
|
||||
__entry->id,
|
||||
__entry->score,
|
||||
__entry->width,
|
||||
__entry->height,
|
||||
__entry->clock >> 16,
|
||||
((__entry->clock & 0xffff) * 1000) >> 16,
|
||||
__entry->color_mode
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(avep_sound_mode,
|
||||
TP_PROTO(struct apple_dcp *dcp, u32 rates, u64 formats, unsigned int nchans),
|
||||
TP_ARGS(dcp, rates, formats, nchans),
|
||||
TP_STRUCT__entry(
|
||||
__field(u64, dcp)
|
||||
__field(u32, rates)
|
||||
__field(u64, formats)
|
||||
__field(unsigned int, nchans)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->dcp = (u64)dcp;
|
||||
__entry->rates = rates;
|
||||
__entry->formats = formats;
|
||||
__entry->nchans = nchans;
|
||||
),
|
||||
TP_printk("dcp=%llx, rates=%#x, formats=%#llx, nchans=%#x",
|
||||
__entry->dcp,
|
||||
__entry->rates,
|
||||
__entry->formats,
|
||||
__entry->nchans
|
||||
)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_DCP_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
|
||||
#include <trace/define_trace.h>
|
15
sys/dev/pci/drm/apple/version_utils.h
Normal file
15
sys/dev/pci/drm/apple/version_utils.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright The Asahi Linux Contributors */
|
||||
|
||||
#ifndef __APPLE_VERSION_UTILS_H__
|
||||
#define __APPLE_VERSION_UTILS_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/args.h>
|
||||
|
||||
#define DCP_FW_UNION(u) (u).DCP_FW
|
||||
#define DCP_FW_SUFFIX CONCATENATE(_, DCP_FW)
|
||||
#define DCP_FW_NAME(name) CONCATENATE(name, DCP_FW_SUFFIX)
|
||||
#define DCP_FW_VERSION(x, y, z) ( ((x) << 16) | ((y) << 8) | (z) )
|
||||
|
||||
#endif /*__APPLE_VERSION_UTILS_H__*/
|
|
@ -1,4 +1,4 @@
|
|||
# $OpenBSD: files.drm,v 1.61 2024/01/16 23:37:51 jsg Exp $
|
||||
# $OpenBSD: files.drm,v 1.62 2024/01/22 18:54:01 kettenis Exp $
|
||||
|
||||
#file dev/pci/drm/aperture.c drm
|
||||
file dev/pci/drm/dma-resv.c drm
|
||||
|
@ -32,6 +32,7 @@ file dev/pci/drm/drm_encoder.c drm
|
|||
file dev/pci/drm/drm_encoder_slave.c drm
|
||||
file dev/pci/drm/drm_exec.c drm
|
||||
file dev/pci/drm/drm_fb_helper.c drm
|
||||
file dev/pci/drm/drm_fb_dma_helper.c drm
|
||||
file dev/pci/drm/drm_fbdev_dma.c drm
|
||||
file dev/pci/drm/drm_fbdev_generic.c drm
|
||||
file dev/pci/drm/drm_file.c drm
|
||||
|
@ -1354,3 +1355,20 @@ file dev/pci/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c amdgpu
|
|||
file dev/pci/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c amdgpu
|
||||
file dev/pci/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c amdgpu
|
||||
file dev/pci/drm/amd/pm/swsmu/smu_cmn.c amdgpu
|
||||
|
||||
device apldcp
|
||||
attach apldcp at fdt
|
||||
file dev/pci/drm/apple/apldcp.c apldcp
|
||||
file dev/pci/drm/apple/afk.c apldcp
|
||||
file dev/pci/drm/apple/dcp_backlight.c apldcp
|
||||
file dev/pci/drm/apple/dptxep.c apldcp
|
||||
file dev/pci/drm/apple/ibootep.c apldcp
|
||||
file dev/pci/drm/apple/iomfb.c apldcp
|
||||
file dev/pci/drm/apple/iomfb_v12_3.c apldcp
|
||||
file dev/pci/drm/apple/iomfb_v13_3.c apldcp
|
||||
file dev/pci/drm/apple/parser.c apldcp
|
||||
file dev/pci/drm/apple/systemep.c apldcp
|
||||
|
||||
device apldrm: drmbase, wsemuldisplaydev, rasops32
|
||||
attach apldrm at fdt
|
||||
file dev/pci/drm/apple/apldrm.c apldrm
|
||||
|
|
|
@ -90,3 +90,7 @@
|
|||
#ifdef __LP64__
|
||||
#define CONFIG_64BIT 1
|
||||
#endif
|
||||
|
||||
#if defined(SUSPEND) || defined(HIBERNATE)
|
||||
#define CONFIG_PM_SLEEP
|
||||
#endif
|
||||
|
|
0
sys/dev/pci/drm/include/linux/apple-mailbox.h
Normal file
0
sys/dev/pci/drm/include/linux/apple-mailbox.h
Normal file
0
sys/dev/pci/drm/include/linux/args.h
Normal file
0
sys/dev/pci/drm/include/linux/args.h
Normal file
32
sys/dev/pci/drm/include/linux/soc/apple/rtkit.h
Normal file
32
sys/dev/pci/drm/include/linux/soc/apple/rtkit.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* Public domain. */
|
||||
|
||||
#ifndef _LINUX_SOC_APPLE_RTKIT_H
|
||||
#define _LINUX_SOC_APPLE_RTKIT_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
struct apple_rtkit;
|
||||
|
||||
struct apple_rtkit_shmem {
|
||||
dma_addr_t iova;
|
||||
void *buffer;
|
||||
size_t size;
|
||||
int is_mapped;
|
||||
};
|
||||
|
||||
struct apple_rtkit_ops {
|
||||
void (*crashed)(void *);
|
||||
void (*recv_message)(void *, uint8_t, uint64_t);
|
||||
int (*shmem_setup)(void *, struct apple_rtkit_shmem *);
|
||||
void (*shmem_destroy)(void *, struct apple_rtkit_shmem *);
|
||||
};
|
||||
|
||||
struct apple_rtkit *devm_apple_rtkit_init(struct device *, void *,
|
||||
const char *, int, const struct apple_rtkit_ops *);
|
||||
|
||||
int apple_rtkit_send_message(struct apple_rtkit *, uint8_t, uint64_t,
|
||||
struct completion *, int);
|
||||
int apple_rtkit_start_ep(struct apple_rtkit *, uint8_t);
|
||||
int apple_rtkit_wake(struct apple_rtkit *);
|
||||
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: syscall_mi.h,v 1.30 2024/01/16 19:05:00 deraadt Exp $ */
|
||||
/* $OpenBSD: syscall_mi.h,v 1.31 2024/01/22 04:38:32 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1982, 1986, 1989, 1993
|
||||
|
@ -102,6 +102,7 @@ pin_check(struct proc *p, register_t code)
|
|||
ktrpinsyscall(p, error, code, addr);
|
||||
#endif
|
||||
KERNEL_LOCK();
|
||||
/* XXX remove or simplify this log() call after SecBSD 1.5 release */
|
||||
log(LOG_ERR,
|
||||
"%s[%d]: pinsyscalls addr %lx code %ld, pinoff 0x%x "
|
||||
"(pin%s %d %lx-%lx %lx) (libcpin%s %d %lx-%lx %lx) error %d\n",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $OpenBSD: Makefile,v 1.15 2016/03/30 06:38:45 jmc Exp $
|
||||
# $OpenBSD: Makefile,v 1.16 2024/01/22 21:07:10 deraadt Exp $
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
|
@ -8,7 +8,7 @@ BINOWN= root
|
|||
BINMODE=4555
|
||||
.PATH: ${.CURDIR}/../../lib/libc/gen
|
||||
LINKS= ${BINDIR}/chpass ${BINDIR}/chfn ${BINDIR}/chpass ${BINDIR}/chsh
|
||||
CFLAGS+=-I${.CURDIR}/../../lib/libc/include
|
||||
CFLAGS+=-I${.CURDIR}/../../lib/libc/include -DFORCE_DBOPEN
|
||||
DPADD+= ${LIBUTIL}
|
||||
LDADD+= -lutil
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $OpenBSD: Makefile,v 1.41 2015/11/26 19:01:47 deraadt Exp $
|
||||
# $OpenBSD: Makefile,v 1.42 2024/01/22 21:07:10 deraadt Exp $
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
|
@ -8,7 +8,7 @@ SRCS= local_passwd.c passwd.c getpwent.c \
|
|||
.PATH: ${.CURDIR}/../../lib/libc/gen
|
||||
DPADD+= ${LIBRPCSVC} ${LIBUTIL}
|
||||
LDADD+= -lrpcsvc -lutil
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
CFLAGS+= -I${.CURDIR} -DFORCE_DBOPEN
|
||||
|
||||
CFLAGS+=-I${.CURDIR}/../../lib/libc/include
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $OpenBSD: style.c,v 1.33 2023/08/17 14:10:28 nicm Exp $ */
|
||||
/* $OpenBSD: style.c,v 1.34 2024/01/22 16:34:46 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
|
@ -242,7 +242,7 @@ style_tostring(struct style *sy)
|
|||
int off = 0;
|
||||
const char *comma = "", *tmp = "";
|
||||
static char s[256];
|
||||
char b[16];
|
||||
char b[21];
|
||||
|
||||
*s = '\0';
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue