1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "graphics_adf.h"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/mman.h>
25 #include <unistd.h>
26 
27 #include <adf/adf.h>
28 #include <sync/sync.h>
29 
30 #include "minui/minui.h"
31 
~GRSurfaceAdf()32 GRSurfaceAdf::~GRSurfaceAdf() {
33   if (mmapped_buffer_) {
34     munmap(mmapped_buffer_, pitch * height);
35   }
36   if (fence_fd != -1) {
37     close(fence_fd);
38   }
39   if (fd != -1) {
40     close(fd);
41   }
42 }
43 
Create(int intf_fd,const drm_mode_modeinfo * mode,__u32 format,int * err)44 std::unique_ptr<GRSurfaceAdf> GRSurfaceAdf::Create(int intf_fd, const drm_mode_modeinfo* mode,
45                                                    __u32 format, int* err) {
46   __u32 offset;
47   __u32 pitch;
48   auto fd = adf_interface_simple_buffer_alloc(intf_fd, mode->hdisplay, mode->vdisplay, format,
49                                               &offset, &pitch);
50 
51   if (fd < 0) {
52     *err = fd;
53     return nullptr;
54   }
55 
56   std::unique_ptr<GRSurfaceAdf> surf = std::unique_ptr<GRSurfaceAdf>(
57       new GRSurfaceAdf(mode->hdisplay, mode->vdisplay, pitch, (format == DRM_FORMAT_RGB565 ? 2 : 4),
58                        offset, pitch, fd));
59 
60   auto mmapped =
61       mmap(nullptr, surf->pitch * surf->height, PROT_WRITE, MAP_SHARED, surf->fd, surf->offset);
62   if (mmapped == MAP_FAILED) {
63     *err = -errno;
64     return nullptr;
65   }
66   surf->mmapped_buffer_ = static_cast<uint8_t*>(mmapped);
67   return surf;
68 }
69 
MinuiBackendAdf()70 MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), current_surface(0), n_surfaces(0) {}
71 
InterfaceInit()72 int MinuiBackendAdf::InterfaceInit() {
73   adf_interface_data intf_data;
74   if (int err = adf_get_interface_data(intf_fd, &intf_data); err < 0) return err;
75 
76   int result = 0;
77   surfaces[0] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result);
78   if (!surfaces[0]) {
79     fprintf(stderr, "Failed to allocate surface 0: %s\n", strerror(-result));
80     goto done;
81   }
82 
83   surfaces[1] = GRSurfaceAdf::Create(intf_fd, &intf_data.current_mode, format, &result);
84   if (!surfaces[1]) {
85     fprintf(stderr, "Failed to allocate surface 1: %s\n", strerror(-result));
86     n_surfaces = 1;
87   } else {
88     n_surfaces = 2;
89   }
90 
91 done:
92   adf_free_interface_data(&intf_data);
93   return result;
94 }
95 
DeviceInit(adf_device * dev)96 int MinuiBackendAdf::DeviceInit(adf_device* dev) {
97   adf_id_t intf_id;
98   int err = adf_find_simple_post_configuration(dev, &format, 1, &intf_id, &eng_id);
99   if (err < 0) return err;
100 
101   err = adf_device_attach(dev, eng_id, intf_id);
102   if (err < 0 && err != -EALREADY) return err;
103 
104   intf_fd = adf_interface_open(dev, intf_id, O_RDWR | O_CLOEXEC);
105   if (intf_fd < 0) return intf_fd;
106 
107   err = InterfaceInit();
108   if (err < 0) {
109     close(intf_fd);
110     intf_fd = -1;
111   }
112 
113   return err;
114 }
115 
Init()116 GRSurface* MinuiBackendAdf::Init() {
117   PixelFormat pixel_format = gr_pixel_format();
118   if (pixel_format == PixelFormat::ABGR) {
119     format = DRM_FORMAT_ABGR8888;
120   } else if (pixel_format == PixelFormat::BGRA) {
121     format = DRM_FORMAT_BGRA8888;
122   } else if (pixel_format == PixelFormat::RGBX) {
123     format = DRM_FORMAT_RGBX8888;
124   } else {
125     format = DRM_FORMAT_RGB565;
126   }
127 
128   adf_id_t* dev_ids = nullptr;
129   ssize_t n_dev_ids = adf_devices(&dev_ids);
130   if (n_dev_ids == 0) {
131     return nullptr;
132   } else if (n_dev_ids < 0) {
133     fprintf(stderr, "enumerating adf devices failed: %s\n", strerror(-n_dev_ids));
134     return nullptr;
135   }
136 
137   intf_fd = -1;
138 
139   for (ssize_t i = 0; i < n_dev_ids && intf_fd < 0; i++) {
140     int err = adf_device_open(dev_ids[i], O_RDWR, &dev);
141     if (err < 0) {
142       fprintf(stderr, "opening adf device %u failed: %s\n", dev_ids[i], strerror(-err));
143       continue;
144     }
145 
146     err = DeviceInit(&dev);
147     if (err < 0) {
148       fprintf(stderr, "initializing adf device %u failed: %s\n", dev_ids[i], strerror(-err));
149       adf_device_close(&dev);
150     }
151   }
152 
153   free(dev_ids);
154 
155   if (intf_fd < 0) return nullptr;
156 
157   GRSurface* ret = Flip();
158 
159   Blank(true);
160   Blank(false);
161 
162   return ret;
163 }
164 
Sync(GRSurfaceAdf * surf)165 void MinuiBackendAdf::Sync(GRSurfaceAdf* surf) {
166   static constexpr unsigned int kWarningTimeout = 3000;
167 
168   if (surf == nullptr) return;
169 
170   if (surf->fence_fd >= 0) {
171     int err = sync_wait(surf->fence_fd, kWarningTimeout);
172     if (err < 0) {
173       perror("adf sync fence wait error\n");
174     }
175 
176     close(surf->fence_fd);
177     surf->fence_fd = -1;
178   }
179 }
180 
Flip()181 GRSurface* MinuiBackendAdf::Flip() {
182   const auto& surf = surfaces[current_surface];
183 
184   int fence_fd = adf_interface_simple_post(intf_fd, eng_id, surf->width, surf->height, format,
185                                            surf->fd, surf->offset, surf->pitch, -1);
186   if (fence_fd >= 0) surf->fence_fd = fence_fd;
187 
188   current_surface = (current_surface + 1) % n_surfaces;
189   Sync(surfaces[current_surface].get());
190   return surfaces[current_surface].get();
191 }
192 
Blank(bool blank)193 void MinuiBackendAdf::Blank(bool blank) {
194   adf_interface_blank(intf_fd, blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON);
195 }
196 
~MinuiBackendAdf()197 MinuiBackendAdf::~MinuiBackendAdf() {
198   adf_device_close(&dev);
199   if (intf_fd >= 0) close(intf_fd);
200 }
201