blob: f78a17b2479821c7833ab83710b19bca50642b2a [file] [log] [blame]
Arkadiusz Hiler2d803c22016-11-25 18:59:35 +01001/*
2 * Copyright © 2016 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
Arkadiusz Hiler2d803c22016-11-25 18:59:35 +010025#include "intel_uc.h"
Sagar Arun Kamblea2695742017-11-16 19:02:41 +053026#include "intel_guc_submission.h"
Michał Winiarski1bbbca02017-12-13 23:13:46 +010027#include "intel_guc.h"
Michal Wajdeczkoddf79d82017-10-04 18:13:42 +000028#include "i915_drv.h"
Arkadiusz Hiler2d803c22016-11-25 18:59:35 +010029
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +010030/* Reset GuC providing us with fresh state for both GuC and HuC.
31 */
32static int __intel_uc_reset_hw(struct drm_i915_private *dev_priv)
33{
34 int ret;
35 u32 guc_status;
36
Michel Thierrycb20a3c2017-10-30 11:56:14 -070037 ret = intel_reset_guc(dev_priv);
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +010038 if (ret) {
Michel Thierrycb20a3c2017-10-30 11:56:14 -070039 DRM_ERROR("Failed to reset GuC, ret = %d\n", ret);
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +010040 return ret;
41 }
42
43 guc_status = I915_READ(GUC_STATUS);
44 WARN(!(guc_status & GS_MIA_IN_RESET),
45 "GuC status: 0x%x, MIA core expected to be in reset\n",
46 guc_status);
47
48 return ret;
49}
50
Michal Wajdeczko121981f2017-12-06 13:53:15 +000051static int __get_platform_enable_guc(struct drm_i915_private *dev_priv)
52{
53 struct intel_uc_fw *guc_fw = &dev_priv->guc.fw;
54 struct intel_uc_fw *huc_fw = &dev_priv->huc.fw;
55 int enable_guc = 0;
56
57 /* Default is to enable GuC/HuC if we know their firmwares */
58 if (intel_uc_fw_is_selected(guc_fw))
59 enable_guc |= ENABLE_GUC_SUBMISSION;
60 if (intel_uc_fw_is_selected(huc_fw))
61 enable_guc |= ENABLE_GUC_LOAD_HUC;
62
63 /* Any platform specific fine-tuning can be done here */
64
65 return enable_guc;
66}
67
Michal Wajdeczko0ed87952018-01-11 15:24:40 +000068static int __get_default_guc_log_level(struct drm_i915_private *dev_priv)
69{
70 int guc_log_level = 0; /* disabled */
71
72 /* Enable if we're running on platform with GuC and debug config */
73 if (HAS_GUC(dev_priv) && intel_uc_is_using_guc() &&
74 (IS_ENABLED(CONFIG_DRM_I915_DEBUG) ||
75 IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)))
76 guc_log_level = 1 + GUC_LOG_VERBOSITY_MAX;
77
78 /* Any platform specific fine-tuning can be done here */
79
80 return guc_log_level;
81}
82
Michal Wajdeczko121981f2017-12-06 13:53:15 +000083/**
84 * intel_uc_sanitize_options - sanitize uC related modparam options
85 * @dev_priv: device private
86 *
87 * In case of "enable_guc" option this function will attempt to modify
88 * it only if it was initially set to "auto(-1)". Default value for this
89 * modparam varies between platforms and it is hardcoded in driver code.
90 * Any other modparam value is only monitored against availability of the
91 * related hardware or firmware definitions.
Michal Wajdeczko0ed87952018-01-11 15:24:40 +000092 *
93 * In case of "guc_log_level" option this function will attempt to modify
94 * it only if it was initially set to "auto(-1)" or if initial value was
95 * "enable(1..4)" on platforms without the GuC. Default value for this
96 * modparam varies between platforms and is usually set to "disable(0)"
97 * unless GuC is enabled on given platform and the driver is compiled with
98 * debug config when this modparam will default to "enable(1..4)".
Michal Wajdeczko121981f2017-12-06 13:53:15 +000099 */
Arkadiusz Hilerd2be9f22017-03-14 15:28:10 +0100100void intel_uc_sanitize_options(struct drm_i915_private *dev_priv)
101{
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000102 struct intel_uc_fw *guc_fw = &dev_priv->guc.fw;
103 struct intel_uc_fw *huc_fw = &dev_priv->huc.fw;
Arkadiusz Hilerb551f612017-03-14 15:28:13 +0100104
Michal Wajdeczkod4a70a12017-03-15 13:37:41 +0000105 /* A negative value means "use platform default" */
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000106 if (i915_modparams.enable_guc < 0)
107 i915_modparams.enable_guc = __get_platform_enable_guc(dev_priv);
Michal Wajdeczkod4a70a12017-03-15 13:37:41 +0000108
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000109 DRM_DEBUG_DRIVER("enable_guc=%d (submission:%s huc:%s)\n",
110 i915_modparams.enable_guc,
111 yesno(intel_uc_is_using_guc_submission()),
112 yesno(intel_uc_is_using_huc()));
113
114 /* Verify GuC firmware availability */
115 if (intel_uc_is_using_guc() && !intel_uc_fw_is_selected(guc_fw)) {
Michal Wajdeczko0ed87952018-01-11 15:24:40 +0000116 DRM_WARN("Incompatible option detected: %s=%d, %s!\n",
117 "enable_guc", i915_modparams.enable_guc,
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000118 !HAS_GUC(dev_priv) ? "no GuC hardware" :
119 "no GuC firmware");
Arkadiusz Hilerb551f612017-03-14 15:28:13 +0100120 }
Michal Wajdeczkod4a70a12017-03-15 13:37:41 +0000121
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000122 /* Verify HuC firmware availability */
123 if (intel_uc_is_using_huc() && !intel_uc_fw_is_selected(huc_fw)) {
Michal Wajdeczko0ed87952018-01-11 15:24:40 +0000124 DRM_WARN("Incompatible option detected: %s=%d, %s!\n",
125 "enable_guc", i915_modparams.enable_guc,
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000126 !HAS_HUC(dev_priv) ? "no HuC hardware" :
127 "no HuC firmware");
128 }
Michal Wajdeczkod4a70a12017-03-15 13:37:41 +0000129
Michal Wajdeczko0ed87952018-01-11 15:24:40 +0000130 /* A negative value means "use platform/config default" */
131 if (i915_modparams.guc_log_level < 0)
132 i915_modparams.guc_log_level =
133 __get_default_guc_log_level(dev_priv);
134
135 if (i915_modparams.guc_log_level > 0 && !intel_uc_is_using_guc()) {
136 DRM_WARN("Incompatible option detected: %s=%d, %s!\n",
137 "guc_log_level", i915_modparams.guc_log_level,
138 !HAS_GUC(dev_priv) ? "no GuC hardware" :
139 "GuC not enabled");
140 i915_modparams.guc_log_level = 0;
141 }
142
143 if (i915_modparams.guc_log_level > 1 + GUC_LOG_VERBOSITY_MAX) {
144 DRM_WARN("Incompatible option detected: %s=%d, %s!\n",
145 "guc_log_level", i915_modparams.guc_log_level,
146 "verbosity too high");
147 i915_modparams.guc_log_level = 1 + GUC_LOG_VERBOSITY_MAX;
148 }
149
150 DRM_DEBUG_DRIVER("guc_log_level=%d (enabled:%s verbosity:%d)\n",
151 i915_modparams.guc_log_level,
152 yesno(i915_modparams.guc_log_level),
153 i915_modparams.guc_log_level - 1);
154
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000155 /* Make sure that sanitization was done */
156 GEM_BUG_ON(i915_modparams.enable_guc < 0);
Michal Wajdeczko0ed87952018-01-11 15:24:40 +0000157 GEM_BUG_ON(i915_modparams.guc_log_level < 0);
Arkadiusz Hilerd2be9f22017-03-14 15:28:10 +0100158}
159
Michal Wajdeczko3af7a9c2017-10-04 15:33:27 +0000160void intel_uc_init_early(struct drm_i915_private *dev_priv)
161{
Michal Wajdeczko9bf384c2017-10-04 18:13:41 +0000162 intel_guc_init_early(&dev_priv->guc);
Michal Wajdeczko2fe2d4e2017-12-06 13:53:10 +0000163 intel_huc_init_early(&dev_priv->huc);
Michal Wajdeczko3af7a9c2017-10-04 15:33:27 +0000164}
165
Arkadiusz Hiler29ad6a32017-03-14 15:28:09 +0100166void intel_uc_init_fw(struct drm_i915_private *dev_priv)
167{
Michal Wajdeczkoa655aeb2017-12-06 13:53:13 +0000168 if (!USES_GUC(dev_priv))
169 return;
170
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000171 if (USES_HUC(dev_priv))
172 intel_uc_fw_fetch(dev_priv, &dev_priv->huc.fw);
173
Michal Wajdeczkoa16b4312017-10-04 15:33:25 +0000174 intel_uc_fw_fetch(dev_priv, &dev_priv->guc.fw);
Arkadiusz Hiler29ad6a32017-03-14 15:28:09 +0100175}
176
Oscar Mateo3950bf32017-03-22 10:39:46 -0700177void intel_uc_fini_fw(struct drm_i915_private *dev_priv)
178{
Michal Wajdeczkoa655aeb2017-12-06 13:53:13 +0000179 if (!USES_GUC(dev_priv))
180 return;
181
Michal Wajdeczkoa16b4312017-10-04 15:33:25 +0000182 intel_uc_fw_fini(&dev_priv->guc.fw);
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000183
184 if (USES_HUC(dev_priv))
185 intel_uc_fw_fini(&dev_priv->huc.fw);
Oscar Mateo3950bf32017-03-22 10:39:46 -0700186}
187
Sagar Arun Kamble1fc556f2017-10-04 15:33:24 +0000188/**
189 * intel_uc_init_mmio - setup uC MMIO access
190 *
191 * @dev_priv: device private
192 *
193 * Setup minimal state necessary for MMIO accesses later in the
194 * initialization sequence.
195 */
196void intel_uc_init_mmio(struct drm_i915_private *dev_priv)
197{
Michal Wajdeczko9bf384c2017-10-04 18:13:41 +0000198 intel_guc_init_send_regs(&dev_priv->guc);
Sagar Arun Kamble1fc556f2017-10-04 15:33:24 +0000199}
200
Daniele Ceraolo Spurioac58d2a2017-05-22 10:50:28 -0700201static void guc_capture_load_err_log(struct intel_guc *guc)
202{
Michal Wajdeczko0ed87952018-01-11 15:24:40 +0000203 if (!guc->log.vma || !i915_modparams.guc_log_level)
Daniele Ceraolo Spurioac58d2a2017-05-22 10:50:28 -0700204 return;
205
206 if (!guc->load_err_log)
207 guc->load_err_log = i915_gem_object_get(guc->log.vma->obj);
208
209 return;
210}
211
212static void guc_free_load_err_log(struct intel_guc *guc)
213{
214 if (guc->load_err_log)
215 i915_gem_object_put(guc->load_err_log);
216}
217
Michal Wajdeczko789a6252017-05-02 10:32:42 +0000218static int guc_enable_communication(struct intel_guc *guc)
219{
Michal Wajdeczkof8a58d62017-05-26 11:13:25 +0000220 struct drm_i915_private *dev_priv = guc_to_i915(guc);
221
Michal Wajdeczkof8a58d62017-05-26 11:13:25 +0000222 if (HAS_GUC_CT(dev_priv))
223 return intel_guc_enable_ct(guc);
224
Michal Wajdeczko789a6252017-05-02 10:32:42 +0000225 guc->send = intel_guc_send_mmio;
226 return 0;
227}
228
229static void guc_disable_communication(struct intel_guc *guc)
230{
Michal Wajdeczkof8a58d62017-05-26 11:13:25 +0000231 struct drm_i915_private *dev_priv = guc_to_i915(guc);
232
233 if (HAS_GUC_CT(dev_priv))
234 intel_guc_disable_ct(guc);
235
Michal Wajdeczko789a6252017-05-02 10:32:42 +0000236 guc->send = intel_guc_send_nop;
237}
238
Michał Winiarski3176ff42017-12-13 23:13:47 +0100239int intel_uc_init_wq(struct drm_i915_private *dev_priv)
240{
241 int ret;
242
243 if (!USES_GUC(dev_priv))
244 return 0;
245
246 ret = intel_guc_init_wq(&dev_priv->guc);
247 if (ret) {
248 DRM_ERROR("Couldn't allocate workqueues for GuC\n");
249 return ret;
250 }
251
252 return 0;
253}
254
255void intel_uc_fini_wq(struct drm_i915_private *dev_priv)
256{
257 if (!USES_GUC(dev_priv))
258 return;
259
Michał Winiarski3176ff42017-12-13 23:13:47 +0100260 intel_guc_fini_wq(&dev_priv->guc);
261}
262
Michał Winiarski61b5c152017-12-13 23:13:48 +0100263int intel_uc_init(struct drm_i915_private *dev_priv)
264{
265 struct intel_guc *guc = &dev_priv->guc;
266 int ret;
267
268 if (!USES_GUC(dev_priv))
269 return 0;
270
271 if (!HAS_GUC(dev_priv))
272 return -ENODEV;
273
274 ret = intel_guc_init(guc);
275 if (ret)
276 return ret;
277
278 if (USES_GUC_SUBMISSION(dev_priv)) {
279 /*
280 * This is stuff we need to have available at fw load time
281 * if we are planning to enable submission later
282 */
283 ret = intel_guc_submission_init(guc);
284 if (ret) {
285 intel_guc_fini(guc);
286 return ret;
287 }
288 }
289
290 return 0;
291}
292
293void intel_uc_fini(struct drm_i915_private *dev_priv)
294{
295 struct intel_guc *guc = &dev_priv->guc;
296
297 if (!USES_GUC(dev_priv))
298 return;
299
300 GEM_BUG_ON(!HAS_GUC(dev_priv));
301
302 if (USES_GUC_SUBMISSION(dev_priv))
303 intel_guc_submission_fini(guc);
304
305 intel_guc_fini(guc);
306}
307
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100308int intel_uc_init_hw(struct drm_i915_private *dev_priv)
309{
Michal Wajdeczko789a6252017-05-02 10:32:42 +0000310 struct intel_guc *guc = &dev_priv->guc;
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000311 struct intel_huc *huc = &dev_priv->huc;
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100312 int ret, attempts;
313
Michal Wajdeczko93ffbe82017-12-06 13:53:12 +0000314 if (!USES_GUC(dev_priv))
Oscar Mateob8991402017-03-28 09:53:47 -0700315 return 0;
316
Michał Winiarski61b5c152017-12-13 23:13:48 +0100317 GEM_BUG_ON(!HAS_GUC(dev_priv));
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000318
Michal Wajdeczko789a6252017-05-02 10:32:42 +0000319 guc_disable_communication(guc);
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100320 gen9_reset_guc_interrupts(dev_priv);
321
daniele.ceraolospurio@intel.com13f6c712017-04-06 17:18:52 -0700322 /* init WOPCM */
323 I915_WRITE(GUC_WOPCM_SIZE, intel_guc_wopcm_size(dev_priv));
324 I915_WRITE(DMA_GUC_WOPCM_OFFSET,
325 GUC_WOPCM_OFFSET_VALUE | HUC_LOADING_AGENT_GUC);
326
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100327 /* WaEnableuKernelHeaderValidFix:skl */
328 /* WaEnableGuCBootHashCheckNotSet:skl,bxt,kbl */
329 if (IS_GEN9(dev_priv))
330 attempts = 3;
331 else
332 attempts = 1;
333
334 while (attempts--) {
335 /*
336 * Always reset the GuC just before (re)loading, so
337 * that the state and timing are fairly predictable
338 */
339 ret = __intel_uc_reset_hw(dev_priv);
340 if (ret)
Michał Winiarski61b5c152017-12-13 23:13:48 +0100341 goto err_out;
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100342
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000343 if (USES_HUC(dev_priv)) {
344 ret = intel_huc_init_hw(huc);
345 if (ret)
Michał Winiarski61b5c152017-12-13 23:13:48 +0100346 goto err_out;
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000347 }
348
Michal Wajdeczko5d53be42017-10-16 14:47:11 +0000349 intel_guc_init_params(guc);
Michal Wajdeczkoe8668bb2017-10-16 14:47:14 +0000350 ret = intel_guc_fw_upload(guc);
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100351 if (ret == 0 || ret != -EAGAIN)
352 break;
353
354 DRM_DEBUG_DRIVER("GuC fw load failed: %d; will reset and "
355 "retry %d more time(s)\n", ret, attempts);
356 }
357
358 /* Did we succeded or run out of retries? */
359 if (ret)
Daniele Ceraolo Spurioac58d2a2017-05-22 10:50:28 -0700360 goto err_log_capture;
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100361
Michal Wajdeczko789a6252017-05-02 10:32:42 +0000362 ret = guc_enable_communication(guc);
363 if (ret)
Daniele Ceraolo Spurioac58d2a2017-05-22 10:50:28 -0700364 goto err_log_capture;
Michal Wajdeczko789a6252017-05-02 10:32:42 +0000365
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000366 if (USES_HUC(dev_priv)) {
367 ret = intel_huc_auth(huc);
368 if (ret)
369 goto err_communication;
370 }
371
Michal Wajdeczko93ffbe82017-12-06 13:53:12 +0000372 if (USES_GUC_SUBMISSION(dev_priv)) {
Michal Wajdeczko0ed87952018-01-11 15:24:40 +0000373 if (i915_modparams.guc_log_level)
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100374 gen9_enable_guc_interrupts(dev_priv);
375
Sagar Arun Kambledb14d0c52017-11-16 19:02:39 +0530376 ret = intel_guc_submission_enable(guc);
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100377 if (ret)
Oscar Mateo3950bf32017-03-22 10:39:46 -0700378 goto err_interrupts;
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100379 }
380
Michal Wajdeczko93ffbe82017-12-06 13:53:12 +0000381 dev_info(dev_priv->drm.dev, "GuC firmware version %u.%u\n",
Michal Wajdeczko86ffc312017-10-16 14:47:17 +0000382 guc->fw.major_ver_found, guc->fw.minor_ver_found);
Michal Wajdeczko93ffbe82017-12-06 13:53:12 +0000383 dev_info(dev_priv->drm.dev, "GuC submission %s\n",
384 enableddisabled(USES_GUC_SUBMISSION(dev_priv)));
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000385 dev_info(dev_priv->drm.dev, "HuC %s\n",
386 enableddisabled(USES_HUC(dev_priv)));
Michal Wajdeczko86ffc312017-10-16 14:47:17 +0000387
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100388 return 0;
389
390 /*
391 * We've failed to load the firmware :(
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100392 */
Oscar Mateo3950bf32017-03-22 10:39:46 -0700393err_interrupts:
394 gen9_disable_guc_interrupts(dev_priv);
Michal Wajdeczko0dfa1ce2017-12-06 13:53:16 +0000395err_communication:
396 guc_disable_communication(guc);
Daniele Ceraolo Spurioac58d2a2017-05-22 10:50:28 -0700397err_log_capture:
398 guc_capture_load_err_log(guc);
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000399err_out:
400 /*
401 * Note that there is no fallback as either user explicitly asked for
402 * the GuC or driver default option was to run with the GuC enabled.
403 */
404 if (GEM_WARN_ON(ret == -EIO))
405 ret = -EINVAL;
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100406
Michal Wajdeczko121981f2017-12-06 13:53:15 +0000407 dev_err(dev_priv->drm.dev, "GuC initialization failed %d\n", ret);
Arkadiusz Hiler6cd5a722017-03-14 15:28:11 +0100408 return ret;
409}
410
Oscar Mateo3950bf32017-03-22 10:39:46 -0700411void intel_uc_fini_hw(struct drm_i915_private *dev_priv)
412{
Sagar Arun Kambledb14d0c52017-11-16 19:02:39 +0530413 struct intel_guc *guc = &dev_priv->guc;
414
415 guc_free_load_err_log(guc);
Michel Thierryc4a89522017-06-05 10:12:51 -0700416
Michal Wajdeczko93ffbe82017-12-06 13:53:12 +0000417 if (!USES_GUC(dev_priv))
Oscar Mateob8991402017-03-28 09:53:47 -0700418 return;
419
Michał Winiarski61b5c152017-12-13 23:13:48 +0100420 GEM_BUG_ON(!HAS_GUC(dev_priv));
421
Michal Wajdeczko93ffbe82017-12-06 13:53:12 +0000422 if (USES_GUC_SUBMISSION(dev_priv))
Sagar Arun Kambledb14d0c52017-11-16 19:02:39 +0530423 intel_guc_submission_disable(guc);
Michal Wajdeczko2f640852017-05-26 11:13:24 +0000424
Sagar Arun Kambledb14d0c52017-11-16 19:02:39 +0530425 guc_disable_communication(guc);
Michal Wajdeczko2f640852017-05-26 11:13:24 +0000426
Michał Winiarski61b5c152017-12-13 23:13:48 +0100427 if (USES_GUC_SUBMISSION(dev_priv))
Oscar Mateo3950bf32017-03-22 10:39:46 -0700428 gen9_disable_guc_interrupts(dev_priv);
Oscar Mateo3950bf32017-03-22 10:39:46 -0700429}