]> Git Repo - J-linux.git/blob - sound/pci/emu10k1/io.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[J-linux.git] / sound / pci / emu10k1 / io.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (c) by Jaroslav Kysela <[email protected]>
4  *                   Creative Labs, Inc.
5  *  Routines for control of EMU10K1 chips
6  *
7  *  BUGS:
8  *    --
9  *
10  *  TODO:
11  *    --
12  */
13
14 #include <linux/time.h>
15 #include <sound/core.h>
16 #include <sound/emu10k1.h>
17 #include <linux/delay.h>
18 #include <linux/export.h>
19 #include "p17v.h"
20
21 unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
22 {
23         unsigned long flags;
24         unsigned int regptr, val;
25         unsigned int mask;
26
27         mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
28         regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
29
30         if (reg & 0xff000000) {
31                 unsigned char size, offset;
32                 
33                 size = (reg >> 24) & 0x3f;
34                 offset = (reg >> 16) & 0x1f;
35                 mask = ((1 << size) - 1) << offset;
36                 
37                 spin_lock_irqsave(&emu->emu_lock, flags);
38                 outl(regptr, emu->port + PTR);
39                 val = inl(emu->port + DATA);
40                 spin_unlock_irqrestore(&emu->emu_lock, flags);
41                 
42                 return (val & mask) >> offset;
43         } else {
44                 spin_lock_irqsave(&emu->emu_lock, flags);
45                 outl(regptr, emu->port + PTR);
46                 val = inl(emu->port + DATA);
47                 spin_unlock_irqrestore(&emu->emu_lock, flags);
48                 return val;
49         }
50 }
51
52 EXPORT_SYMBOL(snd_emu10k1_ptr_read);
53
54 void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
55 {
56         unsigned int regptr;
57         unsigned long flags;
58         unsigned int mask;
59
60         if (snd_BUG_ON(!emu))
61                 return;
62         mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
63         regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
64
65         if (reg & 0xff000000) {
66                 unsigned char size, offset;
67
68                 size = (reg >> 24) & 0x3f;
69                 offset = (reg >> 16) & 0x1f;
70                 mask = ((1 << size) - 1) << offset;
71                 data = (data << offset) & mask;
72
73                 spin_lock_irqsave(&emu->emu_lock, flags);
74                 outl(regptr, emu->port + PTR);
75                 data |= inl(emu->port + DATA) & ~mask;
76                 outl(data, emu->port + DATA);
77                 spin_unlock_irqrestore(&emu->emu_lock, flags);          
78         } else {
79                 spin_lock_irqsave(&emu->emu_lock, flags);
80                 outl(regptr, emu->port + PTR);
81                 outl(data, emu->port + DATA);
82                 spin_unlock_irqrestore(&emu->emu_lock, flags);
83         }
84 }
85
86 EXPORT_SYMBOL(snd_emu10k1_ptr_write);
87
88 unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, 
89                                           unsigned int reg, 
90                                           unsigned int chn)
91 {
92         unsigned long flags;
93         unsigned int regptr, val;
94   
95         regptr = (reg << 16) | chn;
96
97         spin_lock_irqsave(&emu->emu_lock, flags);
98         outl(regptr, emu->port + PTR2);
99         val = inl(emu->port + DATA2);
100         spin_unlock_irqrestore(&emu->emu_lock, flags);
101         return val;
102 }
103
104 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, 
105                                    unsigned int reg, 
106                                    unsigned int chn, 
107                                    unsigned int data)
108 {
109         unsigned int regptr;
110         unsigned long flags;
111
112         regptr = (reg << 16) | chn;
113
114         spin_lock_irqsave(&emu->emu_lock, flags);
115         outl(regptr, emu->port + PTR2);
116         outl(data, emu->port + DATA2);
117         spin_unlock_irqrestore(&emu->emu_lock, flags);
118 }
119
120 int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
121                                    unsigned int data)
122 {
123         unsigned int reset, set;
124         unsigned int reg, tmp;
125         int n, result;
126         int err = 0;
127
128         /* This function is not re-entrant, so protect against it. */
129         spin_lock(&emu->spi_lock);
130         if (emu->card_capabilities->ca0108_chip)
131                 reg = P17V_SPI;
132         else {
133                 /* For other chip types the SPI register
134                  * is currently unknown. */
135                 err = 1;
136                 goto spi_write_exit;
137         }
138         if (data > 0xffff) {
139                 /* Only 16bit values allowed */
140                 err = 1;
141                 goto spi_write_exit;
142         }
143
144         tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
145         reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
146         set = reset | 0x10000; /* Set xxx1xxxx */
147         snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
148         tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
149         snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
150         result = 1;
151         /* Wait for status bit to return to 0 */
152         for (n = 0; n < 100; n++) {
153                 udelay(10);
154                 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
155                 if (!(tmp & 0x10000)) {
156                         result = 0;
157                         break;
158                 }
159         }
160         if (result) {
161                 /* Timed out */
162                 err = 1;
163                 goto spi_write_exit;
164         }
165         snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
166         tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
167         err = 0;
168 spi_write_exit:
169         spin_unlock(&emu->spi_lock);
170         return err;
171 }
172
173 /* The ADC does not support i2c read, so only write is implemented */
174 int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
175                                 u32 reg,
176                                 u32 value)
177 {
178         u32 tmp;
179         int timeout = 0;
180         int status;
181         int retry;
182         int err = 0;
183
184         if ((reg > 0x7f) || (value > 0x1ff)) {
185                 dev_err(emu->card->dev, "i2c_write: invalid values.\n");
186                 return -EINVAL;
187         }
188
189         /* This function is not re-entrant, so protect against it. */
190         spin_lock(&emu->i2c_lock);
191
192         tmp = reg << 25 | value << 16;
193
194         /* This controls the I2C connected to the WM8775 ADC Codec */
195         snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
196         tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
197
198         for (retry = 0; retry < 10; retry++) {
199                 /* Send the data to i2c */
200                 tmp = 0;
201                 tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
202                 snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
203
204                 /* Wait till the transaction ends */
205                 while (1) {
206                         mdelay(1);
207                         status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
208                         timeout++;
209                         if ((status & I2C_A_ADC_START) == 0)
210                                 break;
211
212                         if (timeout > 1000) {
213                                 dev_warn(emu->card->dev,
214                                            "emu10k1:I2C:timeout status=0x%x\n",
215                                            status);
216                                 break;
217                         }
218                 }
219                 //Read back and see if the transaction is successful
220                 if ((status & I2C_A_ADC_ABORT) == 0)
221                         break;
222         }
223
224         if (retry == 10) {
225                 dev_err(emu->card->dev, "Writing to ADC failed!\n");
226                 dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
227                         status, reg, value);
228                 /* dump_stack(); */
229                 err = -EINVAL;
230         }
231     
232         spin_unlock(&emu->i2c_lock);
233         return err;
234 }
235
236 void snd_emu1010_fpga_write(struct snd_emu10k1 *emu, u32 reg, u32 value)
237 {
238         unsigned long flags;
239
240         if (snd_BUG_ON(reg > 0x3f))
241                 return;
242         reg += 0x40; /* 0x40 upwards are registers. */
243         if (snd_BUG_ON(value > 0x3f)) /* 0 to 0x3f are values */
244                 return;
245         spin_lock_irqsave(&emu->emu_lock, flags);
246         outw(reg, emu->port + A_GPIO);
247         udelay(10);
248         outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
249         udelay(10);
250         outw(value, emu->port + A_GPIO);
251         udelay(10);
252         outw(value | 0x80 , emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
253         spin_unlock_irqrestore(&emu->emu_lock, flags);
254 }
255
256 void snd_emu1010_fpga_read(struct snd_emu10k1 *emu, u32 reg, u32 *value)
257 {
258         // The higest input pin is used as the designated interrupt trigger,
259         // so it needs to be masked out.
260         u32 mask = emu->card_capabilities->ca0108_chip ? 0x1f : 0x7f;
261         unsigned long flags;
262         if (snd_BUG_ON(reg > 0x3f))
263                 return;
264         reg += 0x40; /* 0x40 upwards are registers. */
265         spin_lock_irqsave(&emu->emu_lock, flags);
266         outw(reg, emu->port + A_GPIO);
267         udelay(10);
268         outw(reg | 0x80, emu->port + A_GPIO);  /* High bit clocks the value into the fpga. */
269         udelay(10);
270         *value = ((inw(emu->port + A_GPIO) >> 8) & mask);
271         spin_unlock_irqrestore(&emu->emu_lock, flags);
272 }
273
274 /* Each Destination has one and only one Source,
275  * but one Source can feed any number of Destinations simultaneously.
276  */
277 void snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 *emu, u32 dst, u32 src)
278 {
279         if (snd_BUG_ON(dst & ~0x71f))
280                 return;
281         if (snd_BUG_ON(src & ~0x71f))
282                 return;
283         snd_emu1010_fpga_write(emu, EMU_HANA_DESTHI, dst >> 8);
284         snd_emu1010_fpga_write(emu, EMU_HANA_DESTLO, dst & 0x1f);
285         snd_emu1010_fpga_write(emu, EMU_HANA_SRCHI, src >> 8);
286         snd_emu1010_fpga_write(emu, EMU_HANA_SRCLO, src & 0x1f);
287 }
288
289 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
290 {
291         unsigned long flags;
292         unsigned int enable;
293
294         spin_lock_irqsave(&emu->emu_lock, flags);
295         enable = inl(emu->port + INTE) | intrenb;
296         outl(enable, emu->port + INTE);
297         spin_unlock_irqrestore(&emu->emu_lock, flags);
298 }
299
300 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
301 {
302         unsigned long flags;
303         unsigned int enable;
304
305         spin_lock_irqsave(&emu->emu_lock, flags);
306         enable = inl(emu->port + INTE) & ~intrenb;
307         outl(enable, emu->port + INTE);
308         spin_unlock_irqrestore(&emu->emu_lock, flags);
309 }
310
311 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
312 {
313         unsigned long flags;
314         unsigned int val;
315
316         spin_lock_irqsave(&emu->emu_lock, flags);
317         if (voicenum >= 32) {
318                 outl(CLIEH << 16, emu->port + PTR);
319                 val = inl(emu->port + DATA);
320                 val |= 1 << (voicenum - 32);
321         } else {
322                 outl(CLIEL << 16, emu->port + PTR);
323                 val = inl(emu->port + DATA);
324                 val |= 1 << voicenum;
325         }
326         outl(val, emu->port + DATA);
327         spin_unlock_irqrestore(&emu->emu_lock, flags);
328 }
329
330 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
331 {
332         unsigned long flags;
333         unsigned int val;
334
335         spin_lock_irqsave(&emu->emu_lock, flags);
336         if (voicenum >= 32) {
337                 outl(CLIEH << 16, emu->port + PTR);
338                 val = inl(emu->port + DATA);
339                 val &= ~(1 << (voicenum - 32));
340         } else {
341                 outl(CLIEL << 16, emu->port + PTR);
342                 val = inl(emu->port + DATA);
343                 val &= ~(1 << voicenum);
344         }
345         outl(val, emu->port + DATA);
346         spin_unlock_irqrestore(&emu->emu_lock, flags);
347 }
348
349 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
350 {
351         unsigned long flags;
352
353         spin_lock_irqsave(&emu->emu_lock, flags);
354         if (voicenum >= 32) {
355                 outl(CLIPH << 16, emu->port + PTR);
356                 voicenum = 1 << (voicenum - 32);
357         } else {
358                 outl(CLIPL << 16, emu->port + PTR);
359                 voicenum = 1 << voicenum;
360         }
361         outl(voicenum, emu->port + DATA);
362         spin_unlock_irqrestore(&emu->emu_lock, flags);
363 }
364
365 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
366 {
367         unsigned long flags;
368         unsigned int val;
369
370         spin_lock_irqsave(&emu->emu_lock, flags);
371         if (voicenum >= 32) {
372                 outl(HLIEH << 16, emu->port + PTR);
373                 val = inl(emu->port + DATA);
374                 val |= 1 << (voicenum - 32);
375         } else {
376                 outl(HLIEL << 16, emu->port + PTR);
377                 val = inl(emu->port + DATA);
378                 val |= 1 << voicenum;
379         }
380         outl(val, emu->port + DATA);
381         spin_unlock_irqrestore(&emu->emu_lock, flags);
382 }
383
384 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
385 {
386         unsigned long flags;
387         unsigned int val;
388
389         spin_lock_irqsave(&emu->emu_lock, flags);
390         if (voicenum >= 32) {
391                 outl(HLIEH << 16, emu->port + PTR);
392                 val = inl(emu->port + DATA);
393                 val &= ~(1 << (voicenum - 32));
394         } else {
395                 outl(HLIEL << 16, emu->port + PTR);
396                 val = inl(emu->port + DATA);
397                 val &= ~(1 << voicenum);
398         }
399         outl(val, emu->port + DATA);
400         spin_unlock_irqrestore(&emu->emu_lock, flags);
401 }
402
403 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
404 {
405         unsigned long flags;
406
407         spin_lock_irqsave(&emu->emu_lock, flags);
408         if (voicenum >= 32) {
409                 outl(HLIPH << 16, emu->port + PTR);
410                 voicenum = 1 << (voicenum - 32);
411         } else {
412                 outl(HLIPL << 16, emu->port + PTR);
413                 voicenum = 1 << voicenum;
414         }
415         outl(voicenum, emu->port + DATA);
416         spin_unlock_irqrestore(&emu->emu_lock, flags);
417 }
418
419 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
420 {
421         unsigned long flags;
422         unsigned int sol;
423
424         spin_lock_irqsave(&emu->emu_lock, flags);
425         if (voicenum >= 32) {
426                 outl(SOLEH << 16, emu->port + PTR);
427                 sol = inl(emu->port + DATA);
428                 sol |= 1 << (voicenum - 32);
429         } else {
430                 outl(SOLEL << 16, emu->port + PTR);
431                 sol = inl(emu->port + DATA);
432                 sol |= 1 << voicenum;
433         }
434         outl(sol, emu->port + DATA);
435         spin_unlock_irqrestore(&emu->emu_lock, flags);
436 }
437
438 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
439 {
440         unsigned long flags;
441         unsigned int sol;
442
443         spin_lock_irqsave(&emu->emu_lock, flags);
444         if (voicenum >= 32) {
445                 outl(SOLEH << 16, emu->port + PTR);
446                 sol = inl(emu->port + DATA);
447                 sol &= ~(1 << (voicenum - 32));
448         } else {
449                 outl(SOLEL << 16, emu->port + PTR);
450                 sol = inl(emu->port + DATA);
451                 sol &= ~(1 << voicenum);
452         }
453         outl(sol, emu->port + DATA);
454         spin_unlock_irqrestore(&emu->emu_lock, flags);
455 }
456
457 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
458 {
459         volatile unsigned count;
460         unsigned int newtime = 0, curtime;
461
462         curtime = inl(emu->port + WC) >> 6;
463         while (wait-- > 0) {
464                 count = 0;
465                 while (count++ < 16384) {
466                         newtime = inl(emu->port + WC) >> 6;
467                         if (newtime != curtime)
468                                 break;
469                 }
470                 if (count > 16384)
471                         break;
472                 curtime = newtime;
473         }
474 }
475
476 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
477 {
478         struct snd_emu10k1 *emu = ac97->private_data;
479         unsigned long flags;
480         unsigned short val;
481
482         spin_lock_irqsave(&emu->emu_lock, flags);
483         outb(reg, emu->port + AC97ADDRESS);
484         val = inw(emu->port + AC97DATA);
485         spin_unlock_irqrestore(&emu->emu_lock, flags);
486         return val;
487 }
488
489 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
490 {
491         struct snd_emu10k1 *emu = ac97->private_data;
492         unsigned long flags;
493
494         spin_lock_irqsave(&emu->emu_lock, flags);
495         outb(reg, emu->port + AC97ADDRESS);
496         outw(data, emu->port + AC97DATA);
497         spin_unlock_irqrestore(&emu->emu_lock, flags);
498 }
499
500 /*
501  *  convert rate to pitch
502  */
503
504 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
505 {
506         static const u32 logMagTable[128] = {
507                 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
508                 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
509                 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
510                 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
511                 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
512                 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
513                 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
514                 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
515                 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
516                 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
517                 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
518                 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
519                 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
520                 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
521                 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
522                 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
523         };
524         static const char logSlopeTable[128] = {
525                 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
526                 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
527                 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
528                 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
529                 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
530                 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
531                 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
532                 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
533                 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
534                 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
535                 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
536                 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
537                 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
538                 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
539                 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
540                 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
541         };
542         int i;
543
544         if (rate == 0)
545                 return 0;       /* Bail out if no leading "1" */
546         rate *= 11185;          /* Scale 48000 to 0x20002380 */
547         for (i = 31; i > 0; i--) {
548                 if (rate & 0x80000000) {        /* Detect leading "1" */
549                         return (((unsigned int) (i - 15) << 20) +
550                                logMagTable[0x7f & (rate >> 24)] +
551                                         (0x7f & (rate >> 17)) *
552                                         logSlopeTable[0x7f & (rate >> 24)]);
553                 }
554                 rate <<= 1;
555         }
556
557         return 0;               /* Should never reach this point */
558 }
559
This page took 0.089906 seconds and 4 git commands to generate.